1 Getting started

To copy the code, click the button in the upper right corner of the code-chunks.

1.1 clean up

rm(list = ls())
gc()


1.2 general custom functions

  • fpackage.check: Check if packages are installed (and install if not) in R
  • fsave: Function to save data with time stamp in correct directory
  • fload: Load R-objects under new names
  • fshowdf: Print objects (tibble / data.frame) nicely on screen in .Rmd.
  • fplot_graph: visualize the network topology and agent positions (degree-trait correlation)
fpackage.check <- function(packages) {
    lapply(packages, FUN = function(x) {
        if (!require(x, character.only = TRUE)) {
            install.packages(x, dependencies = TRUE)
            library(x, character.only = TRUE)
        }
    })
}

fsave <- function(x, file, location = "./data/processed/", ...) {
    if (!dir.exists(location))
        dir.create(location)
    datename <- substr(gsub("[:-]", "", Sys.time()), 1, 8)
    totalname <- paste(location, datename, file, sep = "")
    print(paste("SAVED: ", totalname, sep = ""))
    save(x, file = totalname)
}

fload <- function(fileName) {
    load(fileName)
    get(ls()[ls() != "fileName"])
}

fshowdf <- function(x, ...) {
    knitr::kable(x, digits = 2, "html", ...) %>%
        kableExtra::kable_styling(bootstrap_options = c("striped", "hover")) %>%
        kableExtra::scroll_box(width = "100%", height = "300px")
}

fplot_graph <- function(graph, main=NULL, layout_algo=NULL, 
                        col1 = "#FFD700", col2 = "#800080") {
  plot(graph,
       main = main,
       layout = layout_algo,
       vertex.label = NA,
       vertex.size = degree(graph) + 2, # node size based on degree
       vertex.color = ifelse(V(graph)$role == "trendsetter", col1, col2),
       edge.width = 0.5,
       edge.color = "darkgrey")
  #add legend
  legend("bottomleft",
         legend = c("Trendsetter", "Conformist"),
         pch = 21,
         col = c(col1,col2),
         pt.bg = c(col1,col2),
         pt.cex = 3,
         bty = "n")
}


1.3 necessary packages

  • tidyverse
  • plotly
  • igraph
  • viplot
  • ggplot2
  • ggpubr
  • plotly
  • parallel
  • foreach
  • doParallel
packages = c("tidyverse", "igraph", "vioplot", "ggplot2", "ggpubr", "plotly", "parallel", "foreach",
    "doParallel")
invisible(fpackage.check(packages))
rm(packages)

2 utility function

A function to calculate the payoff of a specific choice, for an agent (id), given the agents’ current statuses, the network structure, and the utility function parameters:

futility <- function(agent_id, choice, agents, network, params) {
  # get ego and his local neighborhood
  ego <- agents[agent_id, ]
  neighbors <- neighbors(network, ego$id)
  alters <- agents[as.numeric(neighbors), ]
  n <- nrow(alters)

  # count number of neighbors who did (not) follow the trend
  n1 <- sum(alters$choice == 1)
  n0 <- sum(alters$choice == 0)
  
  # calculate expected utility (depending on alters' choices in prior round)
  if(ego$role == "conformist") {
    choice_payoff <- ifelse(choice == 0, params$s, 0)
    coordination_payoff <- ifelse(choice == 0, # normalized by n
                                  (params$w/n) * n0,
                                  (params$z/n) * n1)
  } else {
    choice_payoff <- ifelse(choice == 1, params$e, 0)
    coordination_payoff <- 0
  }
  return(list(utility = choice_payoff + coordination_payoff,
              n1 = n1, n0 = n0))
}

3 network structure

3.1 configuration model

A function to generate a degree sequence based on a power-law / log-normal distribution:

fdegseq <- function(n, dist = "power-law", alpha, k_min = 1, k_max = n - 1, seed = NULL) {

    if (!is.null(seed)) {
        set.seed(seed)
    }

    # generate a degree sequence based on a power-law distribution of the form p(k) ∝ k^{-alpha}

    # create probability distribution
    probs <- (1/(k_min:k_max))^alpha

    # normalize probabilities
    probs <- probs/sum(probs)

    # sample a degree sequence
    degseq <- sample(k_min:k_max, size = n, replace = TRUE, prob = (1/(k_min:k_max))^alpha)

    # correct the degree sequence if its sum is odd (necessary for the configuration model)
    if (sum(degseq)%%2 != 0) {
        degseq[1] <- degseq[1] + 1
    }

    if (dist == "power-law") {
        # if the specified distribution type is power-law, return the degree sequence
        return(degseq)
    } else if (dist == "log-normal") {
        # if the specified distribution type is log-normal, generate a degree sequence following
        # this distribution; but with a same mean degree <k> as its 'scale-free' counterpart.

        # calculate mean degree in sequence
        mean_deg <- mean(degseq)

        # generate raw log-normal degree sequence
        raw_degseq <- rlnorm(n = n, meanlog = log(mean_deg), sdlog = 1)

        # re-scale the degree sequence to match the target mean degree
        scaling_fctr <- mean_deg/mean(raw_degseq)
        scaled_degseq <- raw_degseq * scaling_fctr

        # apply bounds [k_min, k_max] and round to integer values
        bounded_degseq <- pmin(pmax(round(scaled_degseq), k_min), k_max)

        # since the mean may shift after rounding and bounding, re-scale again:
        scaling_fctr2 <- mean_deg/mean(bounded_degseq)
        degseq <- pmin(pmax(round(bounded_degseq * scaling_fctr2), k_min), k_max)

        # correct the degree sequence if its sum is odd (necessary for the configuration model)
        if (sum(degseq)%%2 != 0) {
            degseq[1] <- degseq[1] + 1
        }
        return(degseq)
    } else {
        stop("Invalid distribution type. Please choose 'power-law' or 'log-normal'.")
    }
}
n = 96  # number of agents
p_t = 0.1  # proportion 'trendsetters'

# plotting window
par(mfrow = c(1, 2))

for (i in c("power-law", "log-normal")) {
    # generate degree sequence using a seed
    degseq <- fdegseq(n = n, dist = i, alpha = 2.7, k_min = 3, seed = 123)

    # retrieve <k>
    mean_deg <- mean(degseq)

    # to graph object, based on the Viger-Lapaty algorithm
    network <- sample_degseq(degseq, method = "vl")

    # randomly assign roles to nodes, based on proportion trendsetters
    V(network)$role <- sample(c(rep("trendsetter", floor(n * p_t)), rep("conformist", n - floor(n * p_t))))

    # plot the graph
    fplot_graph(network, main = paste(i, "degree distribution\n", "<k>=", round(mean_deg, 2)))
}

3.2 erdos-renyi random graph

Produce random networks with the same average degree (<k>) as scale-free counterparts (by tweaking edge probability p):

  • <k> = p * (n - 1)
  • p = <k> / (n - 1)
# loop over a higher number of simulated configuration model to get an accurate estimate of average
# degree <k> in scale-free networks
nIter <- 1000
alphas <- c(2.4, 2.7, 3)
kmean <- list()

for (alpha in alphas) {
    kmean[[as.character(alpha)]] <- numeric(nIter)
    for (i in 1:nIter) {
        try({
            degseq <- fdegseq(n = 96, alpha = alpha, k_min = 3)
            network <- sample_degseq(degseq, method = "vl")
            kmean[[as.character(alpha)]][i] <- mean(degree(network))
        }, silent = TRUE)
    }
}

plist <- list(mean(kmean$`2.4`[kmean$`2.4` != 0])/(n - 1), mean(kmean$`2.7`[kmean$`2.7` != 0])/(n - 1),
    mean(kmean$`3`[kmean$`3` != 0])/(n - 1))

par(mfrow = c(1, length(plist)), mar = c(1, 1, 3, 1))
for (i in 1:length(plist)) {
    network <- erdos.renyi.game(96, p = plist[[i]])
    V(network)$role <- sample(c(rep("trendsetter", floor(n * p_t)), rep("conformist", n - floor(n * p_t))))
    fplot_graph(network, main = paste("random network\n", "p =", round(plist[[i]], 3)))
}

3.3 small-world

network <- sample_smallworld(dim = 1, size = 96, nei = 3, p = 0.1)
V(network)$role <- sample(c(rep("trendsetter", floor(n * p_t)), rep("conformist", n - floor(n * p_t))))
fplot_graph(network, main = "small-world network")

# plot degree distributions side-by-side
er <- erdos.renyi.game(n, p = 0.01)
sw <- sample_smallworld(dim = 1, size = n, nei = 3, p = 0.1)
sf <- sample_degseq(fdegseq(n = n, alpha = 2.7, k_min = 3), method = "vl")
ln <- sample_degseq(fdegseq(n = n, alpha = 2.7, dist = "log-normal", k_min = 3), method = "vl")

par(mfrow = c(1, 4))
plot(degree_distribution(er), pch = 20, xlab = "k", ylab = "P(k)", las = 1, main = "Random graph", log = "xy")
plot(degree_distribution(sw), pch = 20, xlab = "k", ylab = "P(k)", las = 1, main = "Small-world graph",
    log = "xy")
plot(degree_distribution(sf), pch = 20, xlab = "k", ylab = "P(k)", las = 1, main = "Scale-free graph",
    log = "xy")
plot(degree_distribution(ln), pch = 20, xlab = "k", ylab = "P(k)", las = 1, main = "Log-normal distribution",
    log = "xy")


3.4 rewiring algorithm

Rewiring algorithm to tune degree (dis)assortativity (Newman 2002):

frewire_r <- function(network, target_r, max_iter = 1e+05, tol = 0.01, verbose = TRUE) {

    current_r <- assortativity_degree(network)
    iteration <- 1

    if (verbose) {
        cat("Target assortativity coefficient:", target_r, "\n")
        cat("Starting assortativity coefficient:", current_r, "\n")
        cat("Tolerance:", tol, "\n")
    }

    while (abs(current_r - target_r) > tol && iteration < max_iter) {

        # get network edges
        edges <- E(network)
        # to edgelist
        edge_list <- ends(network, edges)

        # randomly select two pairs of connected nodes
        idx1 <- sample(1:nrow(edge_list), 1)
        idx2 <- sample(1:nrow(edge_list), 1)

        # extract node indices
        u1 <- edge_list[idx1, 1]  # node 1 of first edge
        v1 <- edge_list[idx1, 2]  # node 2 of first edge
        u2 <- edge_list[idx2, 1]  # etc
        v2 <- edge_list[idx2, 2]

        # check if the two pairs of connected nodes (u1, v1; u2, v2) are disjoint
        if (length(unique(c(u1, v1, u2, v2))) == 4) {
            # check if there is already an edge across the node-pairs ensure no loops and no
            # duplicate edges
            if (!are_adjacent(network, u1, u2) && !are_adjacent(network, v1, v2) && u1 != v2 && u2 !=
                v1) {

                # perform the edge swap (u1,v1) <-> (u2,v2) becomes (u1,v2) <-> (u2,v1)
                new_network <- network  # copy network

                # check if the new edges already exist to avoid duplicates
                if (!are_adjacent(new_network, u1, v2) && !are_adjacent(new_network, u2, v1)) {
                  # add edges
                  new_network <- add_edges(new_network, c(u1, v2, u2, v1))
                  # remove edges
                  new_network <- delete_edges(new_network, get.edge.ids(new_network, c(u1, v1, u2, v2)))

                  # new assortativity
                  new_r <- assortativity_degree(new_network)

                  # accept tie swap if it brings us closer to the target assortativity
                  if (abs(new_r - target_r) < abs(current_r - target_r)) {
                    current_r <- new_r
                    network <- new_network
                    if (verbose) {
                      cat("Rewiring at iteration", iteration, "brought assortativity closer to target! Current assortativity coefficient:",
                        new_r, "\n")
                    }
                  }
                }
            }
        }
        iteration <- iteration + 1
    }

    if (verbose) {
        cat("Final assortativity coefficient:", current_r, "\n")
        if (abs(current_r - target_r) <= tol) {
            cat("Target reached within tolerance.\n")
        } else {
            cat("Reached maximum iterations without meeting target.\n")
        }
    }

    return(network)
}


Apply the function:

# initialize scale-free network
network <- sample_degseq(fdegseq(n = n, alpha = 2.7, k_min = 3, seed = 123), method = "vl")
V(network)$role <- sample(c(rep("trendsetter", floor(n * p_t)), rep("conformist", n - floor(n * p_t))))

# get current assortativity coefficient
assortativity_degree(network)
#> [1] -0.1485165
# set new targets
frewire_r(network, target_r = -0.2, max_iter = 1000)
#> Target assortativity coefficient: -0.2 
#> Starting assortativity coefficient: -0.1485165 
#> Tolerance: 0.01 
#> Rewiring at iteration 4 brought assortativity closer to target! Current assortativity coefficient: -0.1505027 
#> Rewiring at iteration 12 brought assortativity closer to target! Current assortativity coefficient: -0.1506682 
#> Rewiring at iteration 36 brought assortativity closer to target! Current assortativity coefficient: -0.1509165 
#> Rewiring at iteration 45 brought assortativity closer to target! Current assortativity coefficient: -0.151082 
#> Rewiring at iteration 48 brought assortativity closer to target! Current assortativity coefficient: -0.1515786 
#> Rewiring at iteration 56 brought assortativity closer to target! Current assortativity coefficient: -0.1546131 
#> Rewiring at iteration 60 brought assortativity closer to target! Current assortativity coefficient: -0.1557442 
#> Rewiring at iteration 66 brought assortativity closer to target! Current assortativity coefficient: -0.1559097 
#> Rewiring at iteration 82 brought assortativity closer to target! Current assortativity coefficient: -0.1570408 
#> Rewiring at iteration 87 brought assortativity closer to target! Current assortativity coefficient: -0.1570959 
#> Rewiring at iteration 95 brought assortativity closer to target! Current assortativity coefficient: -0.1615098 
#> Rewiring at iteration 99 brought assortativity closer to target! Current assortativity coefficient: -0.1654823 
#> Rewiring at iteration 103 brought assortativity closer to target! Current assortativity coefficient: -0.1665306 
#> Rewiring at iteration 114 brought assortativity closer to target! Current assortativity coefficient: -0.1666409 
#> Rewiring at iteration 117 brought assortativity closer to target! Current assortativity coefficient: -0.1667513 
#> Rewiring at iteration 118 brought assortativity closer to target! Current assortativity coefficient: -0.1668065 
#> Rewiring at iteration 127 brought assortativity closer to target! Current assortativity coefficient: -0.166972 
#> Rewiring at iteration 140 brought assortativity closer to target! Current assortativity coefficient: -0.167303 
#> Rewiring at iteration 147 brought assortativity closer to target! Current assortativity coefficient: -0.1676341 
#> Rewiring at iteration 149 brought assortativity closer to target! Current assortativity coefficient: -0.1677444 
#> Rewiring at iteration 154 brought assortativity closer to target! Current assortativity coefficient: -0.1677996 
#> Rewiring at iteration 162 brought assortativity closer to target! Current assortativity coefficient: -0.1689306 
#> Rewiring at iteration 166 brought assortativity closer to target! Current assortativity coefficient: -0.1689858 
#> Rewiring at iteration 174 brought assortativity closer to target! Current assortativity coefficient: -0.1695927 
#> Rewiring at iteration 179 brought assortativity closer to target! Current assortativity coefficient: -0.1703651 
#> Rewiring at iteration 180 brought assortativity closer to target! Current assortativity coefficient: -0.1726824 
#> Rewiring at iteration 191 brought assortativity closer to target! Current assortativity coefficient: -0.1733445 
#> Rewiring at iteration 204 brought assortativity closer to target! Current assortativity coefficient: -0.1736755 
#> Rewiring at iteration 207 brought assortativity closer to target! Current assortativity coefficient: -0.1757997 
#> Rewiring at iteration 208 brought assortativity closer to target! Current assortativity coefficient: -0.1758549 
#> Rewiring at iteration 210 brought assortativity closer to target! Current assortativity coefficient: -0.1759652 
#> Rewiring at iteration 213 brought assortativity closer to target! Current assortativity coefficient: -0.1762963 
#> Rewiring at iteration 220 brought assortativity closer to target! Current assortativity coefficient: -0.1764618 
#> Rewiring at iteration 242 brought assortativity closer to target! Current assortativity coefficient: -0.1786136 
#> Rewiring at iteration 247 brought assortativity closer to target! Current assortativity coefficient: -0.183993 
#> Rewiring at iteration 267 brought assortativity closer to target! Current assortativity coefficient: -0.1841585 
#> Rewiring at iteration 276 brought assortativity closer to target! Current assortativity coefficient: -0.185262 
#> Rewiring at iteration 281 brought assortativity closer to target! Current assortativity coefficient: -0.1854275 
#> Rewiring at iteration 291 brought assortativity closer to target! Current assortativity coefficient: -0.1877448 
#> Rewiring at iteration 314 brought assortativity closer to target! Current assortativity coefficient: -0.1878551 
#> Rewiring at iteration 317 brought assortativity closer to target! Current assortativity coefficient: -0.1880206 
#> Rewiring at iteration 323 brought assortativity closer to target! Current assortativity coefficient: -0.191331 
#> Final assortativity coefficient: -0.191331 
#> Target reached within tolerance.
#> IGRAPH 5bd224a U--- 96 267 -- Degree sequence random graph
#> + attr: name (g/c), method (g/c), role (v/c)
#> + edges from 5bd224a:
#>   [1]  1--26  1--24  1--10  2--24  2--87  2--31  2--11  2--53  2-- 3  3--24  4--84  4--47  4--61
#>  [14]  4--24  5--24  5--23  5--94  5--38  5--55  5--54  5-- 9  5--58  5--80  5--60  5--11  6--89
#>  [27]  6--81  6--59  7--58  7--53  7--61  8--36  8--67  8--24  8--72  8--65  9--59  9--50 10--11
#>  [40] 10--14 11--95 11--38 11--73 11--35 11--16 11--22 11--47 11--72 11--62 11--24 11--63 12--24
#>  [53] 12--50 13--55 13--24 13--51 14--95 14--32 15--24 15--42 16--93 16--48 16--63 16--73 17--21
#>  [66] 17--69 18--32 18--91 19--87 20--67 20--45 20--65 20--77 20--31 20--50 20--37 20--60 20--87
#>  [79] 20--61 20--24 20--44 21--28 21--42 21--53 21--33 21--22 22--87 23--27 23--37 24--45 24--33
#>  [92] 24--73 24--87 24--54 24--44 24--31 24--74 24--27 24--25 24--89 24--56 24--32 24--83 24--79
#> + ... omitted several edges

3.5 swapping algorithm

Swapping algorithm to tune degree-trait correlation (Lerman, Yan, and Wu 2016):

# we manipulate degree-trait correlation (rho) by swapping attribute values among the nodes. To
# increase \rho_{kx}, we randomly choose nodes v_1 with x=1 and v_0 with x=0 and swap their
# attributes if the degree of the degree of v_0 is greater than that of v_1 (until the desired
# \rho_{kx} is reached; or it no longer changes).
fdegtraitcor <- function(network) {
    roles <- ifelse(V(network)$role == "trendsetter", 1, 0)
    degrees <- degree(network)
    return(list(cor = cor(roles, degrees), roles = roles, degrees = degrees))
}

# swapping function to adjust degree-trait correlation
fswap_rho <- function(network, target_rho, max_iter = 1000, tol = 0.05, verbose = TRUE) {

    current <- fdegtraitcor(network)
    iteration <- 1
    best_network <- network
    best_rho <- current$cor

    if (verbose) {
        cat("Target degree-trait correlation:", target_rho, "\n")
        cat("Starting degree-trait correlation:", current$cor, "\n")
        cat("Tolerance:", tol, "\n\n")
    }

    while (iteration <= max_iter) {
        # check if we are already within tolerance
        if (abs(current$cor - target_rho) <= tol) {
            if (verbose)
                cat("Target reached within tolerance at iteration", iteration, ".\n")
            break
        }

        # randomly select nodes for swapping
        v1 <- sample(which(current$roles == 1), 1)
        v0 <- sample(which(current$roles == 0), 1)

        # get degrees of selected nodes
        k1 <- current$degrees[v1]
        k0 <- current$degrees[v0]

        # swap roles if condition k_v0 > k_v1 is met
        if (k0 > k1) {
            current$roles[v1] <- 0
            current$roles[v0] <- 1

            # update graph roles
            V(network)$role <- ifelse(current$roles == 1, "trendsetter", "conformist")

            # recalculate degree-trait correlation
            current <- fdegtraitcor(network)

            # check if this is the closest correlation to the target so far
            if (abs(current$cor - target_rho) < abs(best_rho - target_rho)) {
                best_network <- network
                best_rho <- current$cor
                if (verbose) {
                  cat("Trait-swapping at iteration", iteration, "brought correlation closer to target! Current correlation:",
                    current$cor, "\n")
                }
            }
        }
        iteration <- iteration + 1
    }

    # check if the final correlation is worse than the best correlation
    final_correlation <- current$cor
    if (abs(final_correlation - target_rho) > abs(best_rho - target_rho)) {
        if (verbose) {
            cat("\nWarning: Final iteration made the correlation worse. Reverting to best observed correlation.\n")
        }
    }

    if (verbose) {
        cat("\nFinal degree-trait correlation:", best_rho, "\n")
        if (abs(best_rho - target_rho) <= tol) {
            cat("Target reached within tolerance.\n")
        } else if (iteration > max_iter) {
            cat("Reached maximum iterations without meeting target.\n")
        }
    }
    return(best_network)
}


Apply the function:

# get current degree-trait correlation
fdegtraitcor(network)
#> $cor
#> [1] -0.1127529
#> 
#> $roles
#>  [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 1 0 0 0
#> [49] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
#> 
#> $degrees
#>  [1]  3  6  3  9 13  3  4  9  4  4 16  4  5  4  3 10  3  3  3 15  9  5  5 44  5  5  4  4  3  3 17 10
#> [33]  5  6  3  4  6  3  3  3  3  3  3  3  3  3  3  4  3  8  3  4  6  3  4  3  3  6  9  3  5  3  3  3
#> [65]  7  4  7  7  6  4  6  5  5  3  4  3  3  4  3  3  3  5  3  6  3  4 28  9  9  3  3  5  3  5  3  3
# set new target
target = 0.5

fdegtraitcor(fswap_rho(network = network, target_rho = target, tol = 0.01))
#> Target degree-trait correlation: 0.5 
#> Starting degree-trait correlation: -0.1127529 
#> Tolerance: 0.01 
#> 
#> Trait-swapping at iteration 1 brought correlation closer to target! Current correlation: -0.06649532 
#> Trait-swapping at iteration 4 brought correlation closer to target! Current correlation: -0.04667063 
#> Trait-swapping at iteration 5 brought correlation closer to target! Current correlation: -0.0400624 
#> Trait-swapping at iteration 8 brought correlation closer to target! Current correlation: -0.03345417 
#> Trait-swapping at iteration 12 brought correlation closer to target! Current correlation: -0.02684594 
#> Trait-swapping at iteration 14 brought correlation closer to target! Current correlation: -0.02023771 
#> Trait-swapping at iteration 18 brought correlation closer to target! Current correlation: -0.007021245 
#> Trait-swapping at iteration 19 brought correlation closer to target! Current correlation: 0.03923637 
#> Trait-swapping at iteration 20 brought correlation closer to target! Current correlation: 0.07227752 
#> Trait-swapping at iteration 22 brought correlation closer to target! Current correlation: 0.08549398 
#> Trait-swapping at iteration 36 brought correlation closer to target! Current correlation: 0.09210221 
#> Trait-swapping at iteration 41 brought correlation closer to target! Current correlation: 0.1846174 
#> Trait-swapping at iteration 88 brought correlation closer to target! Current correlation: 0.257308 
#> Trait-swapping at iteration 102 brought correlation closer to target! Current correlation: 0.3894726 
#> Trait-swapping at iteration 117 brought correlation closer to target! Current correlation: 0.3960808 
#> Trait-swapping at iteration 144 brought correlation closer to target! Current correlation: 0.4092973 
#> Trait-swapping at iteration 244 brought correlation closer to target! Current correlation: 0.429122 
#> Trait-swapping at iteration 260 brought correlation closer to target! Current correlation: 0.4423384 
#> Trait-swapping at iteration 287 brought correlation closer to target! Current correlation: 0.4687714 
#> 
#> Warning: Final iteration made the correlation worse. Reverting to best observed correlation.
#> 
#> Final degree-trait correlation: 0.4687714 
#> Reached maximum iterations without meeting target.
#> $cor
#> [1] 0.4687714
#> 
#> $roles
#>  [1] 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
#> [49] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0
#> 
#> $degrees
#>  [1]  3  6  3  9 13  3  4  9  4  4 16  4  5  4  3 10  3  3  3 15  9  5  5 44  5  5  4  4  3  3 17 10
#> [33]  5  6  3  4  6  3  3  3  3  3  3  3  3  3  3  4  3  8  3  4  6  3  4  3  3  6  9  3  5  3  3  3
#> [65]  7  4  7  7  6  4  6  5  5  3  4  3  3  4  3  3  3  5  3  6  3  4 28  9  9  3  3  5  3  5  3  3


Simulate networks (here, scale-free with alpha = 2.7) with independently varying degree assortativity and degree-trait correlation:

par(mfrow = c(3, 3))

# NETWORKS
alpha = 2.7
degseq <- fdegseq(n = 96, alpha = alpha, k_min = 3, seed = 2352)
network <- sample_degseq(degseq, method = "vl")
V(network)$role <- sample(c(rep("trendsetter", floor(n * p_t)), rep("conformist", n - floor(n * p_t))))

network1 <- frewire_r(network, target_r = -0.1, verbose = FALSE)

fplot_graph(network1, main = paste0("configuration model\ndegree sequence generated from p(k)~k^{-α}\nN=96; prop. trendsetter = 0.1; α=2.7; k_min=3\nr_kk = ",
    round(assortativity_degree(network1), 3), "; p_kx = ", round(fdegtraitcor(network1)$cor, 3)))

network2 <- frewire_r(network, target_r = -0.3, verbose = FALSE)

fplot_graph(network2, main = paste0("configuration model\ndegree sequence generated from p(k)~k^{-α}\nN=96; prop. trendsetter = 0.1; α=2.7; k_min=3\nr_kk = ",
    round(assortativity_degree(network2), 3), "; p_kx = ", round(fdegtraitcor(network2)$cor, 3)))

network3 <- fswap_rho(network2, target_rho = 0.5, verbose = FALSE)

fplot_graph(network3, main = paste0("configuration model\ndegree sequence generated from p(k)~k^{-α}\nN=96; prop. trendsetter = 0.1; α=2.7; k_min=3\nr_kk = ",
    round(assortativity_degree(network3), 3), "; p_kx = ", round(fdegtraitcor(network3)$cor, 3)))

# DEGREE ASSORTATIVITY

plot(degree(network), knn(network)$knn, pch = 19, xlab = "Node degree (k)", ylab = "Average neighbor degree (<k'>)",
    main = "degree assortativity")
plot(degree(network2), knn(network2)$knn, pch = 19, xlab = "Node degree (k)", ylab = "Average neighbor degree (<k'>)",
    main = "degree assortativity")
plot(degree(network3), knn(network3)$knn, pch = 19, xlab = "Node degree (k)", ylab = "Average neighbor degree (<k'>)",
    main = "degree assortativity")

# VIOLIN PLOT OF DEGREE DISTRIBUTION PER ROLE

# extract node degrees
degrees <- degree(network1)
trendsetter_degrees <- degrees[V(network1)$role == "trendsetter"]
conformist_degrees <- degrees[V(network1)$role == "conformist"]

vioplot(trendsetter_degrees, conformist_degrees, names = c("Trendsetters", "Conformists"), col = c("#FFD700",
    "#800080"), main = "degree distribution")

degrees2 <- degree(network2)
trendsetter_degrees2 <- degrees2[V(network2)$role == "trendsetter"]
conformist_degrees2 <- degrees2[V(network2)$role == "conformist"]

vioplot(trendsetter_degrees2, conformist_degrees2, names = c("Trendsetters", "Conformists"), col = c("#FFD700",
    "#800080"), main = "degree distribution")

degrees3 <- degree(network3)
trendsetter_degrees3 <- degrees3[V(network3)$role == "trendsetter"]
conformist_degrees3 <- degrees3[V(network3)$role == "conformist"]
vioplot(trendsetter_degrees3, conformist_degrees3, names = c("Trendsetters", "Conformists"), col = c("#FFD700",
    "#800080"), main = "degree distribution")


Specify a parameter space to generate networks with independently varied degree distribution, degree assortativity, degree-trait correlation:

# structural parameters
n = 96
k_min = 3
p_t = 0.1

# target parameters
alphas <- c(2.4, 2.7, 3)
distributions <- c("power-law", "log-normal")
target_r_values <- list(seq(-0.4, -0.15, by = 0.05), seq(-0.35, -0.05, by = 0.05), seq(-0.3, 0.1, by = 0.05))
names(target_r_values) <- alphas
target_rho_values <- seq(0, 0.6, by = 0.1)

# list for results
results <- list()

for (dist in distributions) {
    for (alpha in alphas) {
        # generate degree sequence from specified distribution and alpha
        degseq <- fdegseq(n = n, dist = dist, alpha = alpha, k_min = k_min, seed = 123)

        # create an undirected, connected, simple graph using Viger-Latapy algorithm
        network <- sample_degseq(degseq, method = "vl")

        # assign roles randomly, based on proportion trendsetter p_t
        V(network)$role <- sample(c(rep("trendsetter", floor(n * p_t)), rep("conformist", n - floor(n *
            p_t))))

        for (target_r in target_r_values[[as.character(alpha)]]) {
            # loop over target_r values
            rewired_network <- frewire_r(network, target_r, verbose = FALSE)
            actual_r <- assortativity_degree(rewired_network)

            for (target_rho in target_rho_values) {
                # loop over target_rho values
                final_network <- fswap_rho(rewired_network, target_rho, verbose = FALSE)
                final_rho <- fdegtraitcor(final_network)$cor

                # and assume that agents act according to their role agents don't observe each
                # others' roles, but do observe actions (based on which they can infer
                # roles/preferences...)

                V(final_network)$action <- ifelse(V(final_network)$role == "trendsetter", 1, 0)

                # store results
                results <- append(results, list(list(distribution = dist, alpha = alpha, target_r = target_r,
                  actual_r = actual_r, target_rho = target_rho, actual_rho = final_rho, network = final_network)))
            }
        }
    }
}


Sample some simulations from the ‘network universe’:

# excluding the graph object:
lapply(sample(results, 5), function(x) {
    z <- x  # copy
    z$network <- NULL
    z
})
#> [[1]]
#> [[1]]$distribution
#> [1] "log-normal"
#> 
#> [[1]]$alpha
#> [1] 2.7
#> 
#> [[1]]$target_r
#> [1] -0.25
#> 
#> [[1]]$actual_r
#> [1] -0.2516837
#> 
#> [[1]]$target_rho
#> [1] 0.5
#> 
#> [[1]]$actual_rho
#> [1] 0.4841843
#> 
#> 
#> [[2]]
#> [[2]]$distribution
#> [1] "log-normal"
#> 
#> [[2]]$alpha
#> [1] 2.7
#> 
#> [[2]]$target_r
#> [1] -0.3
#> 
#> [[2]]$actual_r
#> [1] -0.2920339
#> 
#> [[2]]$target_rho
#> [1] 0.4
#> 
#> [[2]]$actual_rho
#> [1] 0.3513395
#> 
#> 
#> [[3]]
#> [[3]]$distribution
#> [1] "power-law"
#> 
#> [[3]]$alpha
#> [1] 3
#> 
#> [[3]]$target_r
#> [1] -0.25
#> 
#> [[3]]$actual_r
#> [1] -0.240705
#> 
#> [[3]]$target_rho
#> [1] 0.2
#> 
#> [[3]]$actual_rho
#> [1] 0.14159
#> 
#> 
#> [[4]]
#> [[4]]$distribution
#> [1] "power-law"
#> 
#> [[4]]$alpha
#> [1] 3
#> 
#> [[4]]$target_r
#> [1] -0.1
#> 
#> [[4]]$actual_r
#> [1] -0.09125596
#> 
#> [[4]]$target_rho
#> [1] 0.5
#> 
#> [[4]]$actual_rho
#> [1] 0.4929644
#> 
#> 
#> [[5]]
#> [[5]]$distribution
#> [1] "log-normal"
#> 
#> [[5]]$alpha
#> [1] 2.4
#> 
#> [[5]]$target_r
#> [1] -0.2
#> 
#> [[5]]$actual_r
#> [1] -0.1954962
#> 
#> [[5]]$target_rho
#> [1] 0.2
#> 
#> [[5]]$actual_rho
#> [1] 0.1673623

4 “majority illusion”

A function to calculate the magnitude of the “majority illusion”, based on the network structure, and a given majority threshold (here, “weak influence”, so more than half of neighbors need to adopt the trend in order for an ego to be persuaded to do the same):

calculate_majority_illusion <- function(network, threshold = 0.5) {
    roles <- V(network)$role
    actions <- V(network)$action

    # initialize counter for majority illusion
    mi_count <- 0

    # loop over conformists
    for (v in V(network)) {
        if (roles[v] == "conformist") {
            neighbors <- neighbors(network, v)
            trend_neighbors <- sum(actions[neighbors] == 1)
            prop_trend <- trend_neighbors/length(neighbors)

            if (prop_trend > threshold) {
                # under 'strong influence' half of the neighborhood suffices; so set threshold at
                # .49
                mi_count <- mi_count + 1
            }
        }
    }
    # return fraction of conformists who have majority illusion
    return(mi_count/sum(roles == "conformist"))
}


Apply the function over our ‘network universe’, where each network corresponds to a specific parameter space row, generated using a unique seed:

plotdata <- do.call(rbind, lapply(results, function(res) {
    dist <- factor(res$dist, levels = c("power-law", "log-normal"))
    alpha <- res$alpha
    r <- res$actual_r
    rho <- res$actual_rho
    network <- res$network

    # calculate the majority illusion (i.e., the proportion of conformists whose neighbors meet or
    # exceed threshold φ)
    mi <- calculate_majority_illusion(network)

    # create a data-frame
    data.frame(dist = dist, alpha = alpha, r = r, rho = rho, mi = mi)
}))

# make separate data-frames for each level of alpha
alpha1 <- plotdata[plotdata$alpha == unique(plotdata$alpha)[1], ]
alpha2 <- plotdata[plotdata$alpha == unique(plotdata$alpha)[2], ]
alpha3 <- plotdata[plotdata$alpha == unique(plotdata$alpha)[3], ]

# create bins for r (deg-assorativity)
fcreate_bins <- function(data, variable = "r", out = 6) {
    rvals <- data[[variable]]
    quant <- quantile(rvals, probs = seq(0, 1, length.out = out))
    # generate labels dynamically
    labels <- sapply(1:(length(quant) - 1), function(i) {
        paste0(round(quant[i], 2), " < r ≤ ", round(quant[i + 1], 2))
    })
    # add categories to the dataset
    data$r_cats <- cut(rvals, breaks = quant, include.lowest = TRUE, labels = labels)
    return(data)
}

# apply binning to each subset
alpha1 <- fcreate_bins(alpha1)
alpha2 <- fcreate_bins(alpha2)
alpha3 <- fcreate_bins(alpha3)

plot1 <- ggplot(alpha1, aes(x = rho, y = mi, color = as.factor(r_cats))) + facet_wrap(~dist, nrow = 2) +
    geom_point(alpha = 0.7, size = 2) + geom_smooth(se = FALSE, method = "loess") + scale_y_continuous(limits = c(-0.05,
    0.7)) + labs(x = expression("degree-trait correlation (" ~ rho[kx] ~ ")"), y = "prop. conformists w/ prop. trendsetter nbh. > φ",
    color = expression("degree assortativity (" ~ r[kk] ~ ")")) + theme(legend.position = c(0.3, 0.35)) +
    ggtitle(paste0("α=", alpha1$alpha))

plot2 <- ggplot(alpha2, aes(x = rho, y = mi, color = as.factor(r_cats))) + facet_wrap(~dist, nrow = 2) +
    geom_point(alpha = 0.7, size = 2) + geom_smooth(se = FALSE, method = "loess") + scale_y_continuous(limits = c(-0.05,
    0.7)) + labs(x = expression("degree-trait correlation (" ~ rho[kx] ~ ")"), y = "prop. conformists w/ prop. trendsetter nbh. > φ",
    color = expression("degree assortativity (" ~ r[kk] ~ ")")) + theme(legend.position = c(0.3, 0.35)) +
    ggtitle(paste0("α=", alpha2$alpha))

plot3 <- ggplot(alpha3, aes(x = rho, y = mi, color = as.factor(r_cats))) + facet_wrap(~dist, nrow = 2) +
    geom_point(alpha = 0.7, size = 2) + geom_smooth(se = FALSE, method = "loess") + scale_y_continuous(limits = c(-0.05,
    0.7)) + labs(x = expression("degree-trait correlation (" ~ rho[kx] ~ ")"), y = "prop. conformists w/ prop. trendsetter nbh. > φ",
    color = expression("degree assortativity (" ~ r[kk] ~ ")")) + theme(legend.position = c(0.3, 0.35)) +
    ggtitle(paste0("α=", alpha3$alpha))

# combine
ggarrange(plot1, plot2, plot3, ncol = 3) + ggtitle(" \"Majority illusion\" in networks with a power-law and log-normal degree distribution") +
    theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 16))


Now for each parameter space row generate networks over N different seeds:

# set up parallel backend to increase efficiency
ncores <- detectCores() - 1 
cl <- makeCluster(ncores)
registerDoParallel(cl)

# number of seeds
nIter <- 100
distributions <- c("power-law", "log-normal")

# list to store results in
rep_results <- list()

# parallel processing using foreach
rep_results <- foreach(alpha = alphas, .combine = 'c', .packages = c("igraph")) %:%
  foreach(iter = 1:nIter, .combine = 'c', .packages = c("igraph")) %dopar% {
    
    # seed must vary with each iteration
    seed <- 123 + iter
    set.seed(seed)
    
    print(paste0("Running iteration ", iter, "/", nIter, " for alpha = ", alpha))
    
    results <- list()  #temporary storage for the results
    
    # loop over distribution types:
    for (dist in distributions) {
      # generate degree sequence with specified distribution type
      degseq <- fdegseq(n = n, alpha = alpha, k_min = k_min, seed = 123 + iter, dist = dist)
      
      # construct the network
      network <- sample_degseq(degseq, method = "vl")
      
      # assign roles randomly
      V(network)$role <- sample(c(rep("trendsetter", floor(n * p_t)), rep("conformist", n - floor(n * p_t))))
      
      # loop over target_r values
      for (target_r in target_r_values[[as.character(alpha)]]) {
        rewired_network <- frewire_r(network, target_r, verbose = FALSE, max_iter = 1e4)
        actual_r <- assortativity_degree(rewired_network)
        
        # loop over target_rho values
        for (target_rho in target_rho_values) {
          final_network <- fswap_rho(rewired_network, target_rho, verbose = FALSE)
          final_rho <- fdegtraitcor(final_network)$cor
          
          #update choices (choice==role): 
          V(final_network)$action <- ifelse(V(final_network)$role == "trendsetter", 1, 0)
          
          #and calculate global majority illusion
          mi <- calculate_majority_illusion(final_network)
          
          # append results
          results <- append(results, list(list(
            dist = dist,
            alpha = alpha,
            target_r = target_r,
            actual_r = actual_r,
            target_rho = target_rho,
            actual_rho = final_rho,
            mi = mi,
            seed = seed, #store seed for reproducibility
            network = final_network # store networks, so we can access these for the simulations.
        )))
      }
      }
    }
    results
  }

stopCluster(cl)

#save output..
fsave(rep_results, "networks.Rda")


Visualize the extent of majority illusion depending on the network parameters across two degree distribution types, based on network simulations with different seeds. Visualizations include both observed MI and predicted values from OLS regression.

nets <- fload("./data/processed/20250107networks.Rda")

# to a dataframe:
df <- do.call(rbind, lapply(nets, function(res) {
  data.frame(
    seed = res$seed,
    dist = res$dist,
    alpha = res$alpha,
    target_r = res$target_r,
    actual_r = res$actual_r,
    target_rho = res$target_rho,
    actual_rho = res$actual_rho,
    mi = res$mi
  )
}))

# create a 3D scatterplot of observed values of MI, disaggregated by
# degree distribution type
fig1 <- plot_ly()

for (dist in unique(df$dist)) {
  fig1 <- fig1 %>%
    add_trace(
      data = df[df$dist == dist, ],
      x = ~alpha,
      y = ~actual_rho,
      z = ~actual_r,
      color = ~mi,
      type = 'scatter3d',
      mode = 'markers',
      marker = list(size = 3),
      name = as.character(dist),
      hovertemplate = paste(
        "α: %{x}<br>",
        "ρ_{xk}: %{y}<br>",
        "r_{kk}: %{z}<br>",
        "MI: %{marker.color}<extra></extra>"
      )
    ) 
}

# add layout details:
fig1 <- fig1 %>%
  layout(
    title = '3D scatterplot of the magnitude of the "majority illusion" by degree distribution type',
    scene = list(
      xaxis = list(title = "α"),
      yaxis = list(title = "ρ_{xk}"),
      zaxis = list(title = "r_{kk}")
    )
  ) %>%
  colorbar(title="Majority illusion")


#fit models => predicted values
m1 <- lm(mi ~ actual_rho + actual_r + as.factor(alpha), data = df[df$dist == "power-law",])
m2 <- lm(mi ~ actual_rho + actual_r + as.factor(alpha) + actual_rho:actual_r, data = df[df$dist == "power-law",])
m3 <- lm(mi ~ actual_rho + actual_r + as.factor(alpha) + actual_rho:actual_r + actual_rho:as.factor(alpha) + actual_r:as.factor(alpha), data = df[df$dist == "power-law",])

#anova(m1,m2,m3)
#summary(m3)

# create a sequence of values for rho, r, and alpha
rho_vals <- seq(min(df[df$dist == "power-law",]$actual_rho), max(df[df$dist == "power-law",]$actual_rho), length.out = 50)
r_vals <- seq(min(df[df$dist == "power-law",]$actual_r), max(df[df$dist == "power-law",]$actual_r), length.out = 50)
#alpha_vals <- seq(2.4, 3, by=0.1)  
alpha_vals <- c(2.4,2.7,3)

# function to generate the surface data for a given alpha value
fsurfacedat <- function(alpha_val) {
  # create a grid of r and rho values
  grid <- expand.grid(actual_r = r_vals, actual_rho = rho_vals)
  
  # fixed alpha value
  grid$alpha <- alpha_val
  
  # predict the values of mi using the model
  grid$mi_pred <- predict(m3, newdata = grid)
  
  # reshape the predicted values into a matrix
  mi_matrix <- matrix(grid$mi_pred, nrow = length(r_vals), ncol = length(rho_vals), byrow = TRUE)
  
  return(mi_matrix)
}

# precompute the surface data for all alpha values
surface_data_list <- lapply(alpha_vals, fsurfacedat)

# find the global range of MM (z values)
z_min <- min(sapply(surface_data_list, min))
z_max <- max(sapply(surface_data_list, max))

# create the initial surface plot with the first alpha value
fig2 <- plot_ly(
  x = r_vals,  # x-axis: r
  y = rho_vals,  # y-axis: rho
  z = surface_data_list[[1]],  # surface data for the first alpha
  type = "surface",
  colorbar = list(
    title = "MI",
    cmin = z_min, 
    cmax = z_max
  ),
  cmin = z_min,  
  cmax = z_max,
  hovertemplate = paste(
    "r_{kk}: %{x:.2f}<br>",  
    "ρ_{xk}: %{y:.2f}<br>",  
    "MI: %{z:.2f}<extra></extra>"
  )
)

# add slider for dynamic alpha selection
fig2 <- fig2 %>%
  layout(
    title = paste('Predicted "majority illusion" in scale-free network from OLS model for α =', alpha_vals[1]),
    scene = list(
      xaxis = list(title = "r_{kk}"),
      yaxis = list(title = "ρ_{kx}"),
      zaxis = list(
        title = "MI",
        range = c(z_min, z_max) 
      )
    ),
    sliders = list(
      list(
        active = 0,  # start with the first alpha value
        steps = lapply(seq_along(alpha_vals), function(i) {
          list(
            method = "update",
            args = list(
              list(
                z = list(surface_data_list[[i]])  #update surface data
              ),
              list(
                title = paste('Predicted "majority illusion" in scale-free network from OLS model for α =', alpha_vals[i])  # update title
              )
            ),
            label = as.character(alpha_vals[i])  # slider label
          )
        }),
        currentvalue = list(
          prefix = "α: ",  # text displayed next to the slider
          font = list(size = 16)
        )
      )
    )
  )  %>%
  colorbar(title="Majority illusion")

fig1
fig2

5 simulation function

Now that we have our “starting networks”, let’s simulate the evolution of an unpopular norm using our utility function:

fabm <- function(network = network, # the generated network
                 rounds = 10, # number of timesteps/rounds
                 choice_rule = "deterministic", # choice update rule
                 utility_fn = futility, # the utility function
                 params = list(s=10, e=10, w=30, z=35), # utility parameters
                 mi_threshold = ifelse(params$z > 50, .49, .50), # under the "strong influence" condition (i.e., 50<z<90) half of neighbors adopting the trend is sufficient; under the "weak influence" condition (i.e., 30<z<50) *more* than half of neighbors adopting the trend is needed to be influenced.)
                 plot = FALSE ) { # return plot
  
  # make an agents dataframe
  agents <- tibble(
    id = 1:length(network),
    role = V(network)$role,
    preference = ifelse(role == "trendsetter", 1, 0), # 1 = follow trend, 0 = not follow
    choice = NA
  )
  
  # initialize decision history
  decision_history <- tibble()
  
  # simulation loop
  for (t in 1:rounds) {
    if (t == 1) {
      # round 1: agents make decisions based on private preferences (no social information available yet)
      agents <- agents %>%
        mutate(choice = preference)
    } else {
       # round t > 1: agents make decisions based on social information from neighbors (decisions at t-1)
      agents <- agents %>%
        rowwise() %>%
        mutate(
          util_1 = utility_fn(id, 1, agents, network, list(s = params$s, e = params$e, w = params$w, z = params$z))$utility,
          util_0 = utility_fn(id, 0, agents, network, list(s = params$s, e = params$e, w = params$w, z = params$z))$utility,
          # make decision based on expected utility
          choice = ifelse(
            choice_rule == "deterministic",
            ifelse(util_1 > util_0, 1, 0),  # deterministic rule
            sample(c(1, 0), size = 1, prob = c(exp(util_1), exp(util_0)))  # probabilistic rule (@RF: only for conformists? level of noise?)
          )
        )
    }
    # store the decisions for this round
    decision_history <- bind_rows(decision_history, agents %>% 
                                    mutate(round = t))
  }
  
  # based on decision_history:
  
  # 1. calculate global majority illusion over rounds
  globMI <- numeric(rounds)
  for (t in 1:rounds) {
    if (t == 1) {
      # in round 1: no social information, so no MI
      globMI[t] <- NA
    } else {
      # rounds t > 1: calculate magnitude of majority illusion
      # first, make a copy of the network object
      exposure_network <- network
      # update the actions of actors, based on their choices in the previous round
      # after all, actors don't observe others' roles, but only their choices,
      # based on which they can infer their role
      V(exposure_network)$action <- decision_history[decision_history$round == t - 1,]$choice
      globMI[t] <- calculate_majority_illusion(exposure_network, threshold = mi_threshold)  
    }
  }
  
  # to long data frame: 
  MI <- data.frame(
    round = 1:rounds,
    outcome = "majority_illusion",
    statistic = globMI
    )
  
  # 2. calculate the evolution of the unpopular norm
  UN <- decision_history %>%
    group_by(round) %>%
    summarise(
      follow_trend = mean(choice == 1, na.rm = TRUE)
      ) %>%
      pivot_longer(cols = c("follow_trend"),
                   names_to = "outcome",
                   values_to = "statistic")
  
  # bind
  plotdata <- rbind(MI, UN)
  
  fig <- ggplot(plotdata, aes(x=round, y=statistic, color=factor(outcome))) +
      geom_line() +
      geom_point() +
      scale_y_continuous(labels = scales::percent_format(scale = 100), limits = c(0,1)) +
      scale_x_continuous(breaks = seq(1, max(plotdata$round), by = 1)) +
     labs(
       title = "Evolution of an unpopular norm",
       subtitle = "`follow_trend` denotes the percentage of all agents that follow the trend.\n`majority_illusion` reflects the percentage of conformists whose neighbors meet or exceed the adoption threshold φ (i.e., 0.50),\nwith 'strong influence' referring to both meeting and exceeding the threshold, and 'weak influence' to exceeding it only.\nThe grey dashed line reflects the percentage of agents whose role is trendsetter.",
       x = "round",
       y = "% agents",
       color = "outcome") +
     theme(panel.grid.minor.x = element_blank(),
            #legend.position = "bottom",
            plot.subtitle = element_text(size = 8)) +
    geom_hline(yintercept = prop.table(table(agents$role))[2], linetype = "dashed", color = "darkgrey", size = 1)
   
   if(plot==TRUE) {
     return(list(decision_history=decision_history,
                 evo=plotdata,
                 plot=fig))
   } else {
     return(list(decision_history=decision_history,
                 evo=plotdata
                 ))
   }
}


5.1 example

Select random network from the networks with the top 10% highest majority illusion and simulate the evolution of the trend:

set.seed(235435)  # set seed for reproducibility 

mis <- sapply(nets, function(x) x$mi)  # extract MI values
sorted <- order(mis)  # sort indices by MI
n <- length(mis)

top10 <- sorted[ceiling(0.9 * n):n]  # top 10% MI
ind <- sample(top10, 10)  # select random elements

# loop through each selection network; inspect the evolution of MI and UP, for both weak and strong
# influence scenarios
for (i in ind) {
    # access network and statistics
    network <- nets[[i]]$network
    dist <- nets[[i]]$dist
    alpha <- nets[[i]]$alpha
    actual_r <- nets[[i]]$actual_r
    actual_rho <- nets[[i]]$actual_rho
    mi <- nets[[i]]$mi

    # print statistics
    cat("Selected network", i, "\n")
    cat("Distribution:", dist, "\n")
    if (dist == "power-law") {
        cat("α:", alpha, "\n")
    }
    cat("r_{kk}:", actual_r, "\n")
    cat("ρ_{kx}:", actual_rho, "\n")
    cat("Starting MI:", mi, "\n")

    # weak influence
    cat("Running simulation for \"weak influence\" scenario...\n")
    print(fabm(network = network, rounds = 20, params = list(s = 10, e = 10, w = 30, z = 40), plot = TRUE)$plot)

    # strong influence
    cat("Running simulation for \"strong influence\" scenario...\n")
    print(fabm(network = network, rounds = 20, params = list(s = 10, e = 10, w = 30, z = 60), plot = TRUE)$plot)
}
#> Selected network 15753 
#> Distribution: power-law 
#> α: 2.7 
#> r_{kk}: -0.2478605 
#> ρ_{kx}: 0.1802289 
#> Starting MI: 0.1609195 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 4900 
#> Distribution: power-law 
#> α: 2.4 
#> r_{kk}: -0.2403668 
#> ρ_{kx}: 0.5679784 
#> Starting MI: 0.2068966 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 17338 
#> Distribution: power-law 
#> α: 2.7 
#> r_{kk}: -0.361668 
#> ρ_{kx}: 0.4864376 
#> Starting MI: 0.2183908 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 2877 
#> Distribution: power-law 
#> α: 2.4 
#> r_{kk}: -0.3051902 
#> ρ_{kx}: 0.5580024 
#> Starting MI: 0.1724138 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 13566 
#> Distribution: log-normal 
#> r_{kk}: -0.2407299 
#> ρ_{kx}: 0.5754819 
#> Starting MI: 0.183908 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 6265 
#> Distribution: log-normal 
#> r_{kk}: -0.3903671 
#> ρ_{kx}: 0.556112 
#> Starting MI: 0.1954023 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 139 
#> Distribution: log-normal 
#> r_{kk}: -0.341074 
#> ρ_{kx}: 0.5159114 
#> Starting MI: 0.1724138 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 10364 
#> Distribution: power-law 
#> α: 2.7 
#> r_{kk}: -0.3400668 
#> ρ_{kx}: 0.424732 
#> Starting MI: 0.2758621 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 7749 
#> Distribution: power-law 
#> α: 2.4 
#> r_{kk}: -0.4913325 
#> ρ_{kx}: 0.6382822 
#> Starting MI: 0.5402299 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...

#> Selected network 385 
#> Distribution: log-normal 
#> r_{kk}: -0.3900845 
#> ρ_{kx}: 0.5710399 
#> Starting MI: 0.5517241 
#> Running simulation for "weak influence" scenario...

#> Running simulation for "strong influence" scenario...


6 simulations

# clean our working environement, but keep our functions, our dataframe, and our list of starting
# networks
rm(list = setdiff(ls(), c("df", "nets", lsf.str())))
gc()

# @RF: let's start not with all networks... but select for each parameter space row a few seeds
sub_nets <- Filter(function(x) x$seed %in% c(124), nets)

# parameters
rounds = 10
z1 = 40  # weak influence
z2 = 60  # strong influence

# parallel backend
ncores <- detectCores() - 1
cl <- makeCluster(ncores)
registerDoParallel(cl)

# export custom functions and required objects
clusterExport(cl, c("fabm", "futility", "sub_nets", "rounds", "z1", "z2"))

# run loop in parallel
system.time({
    results <- foreach(i = 1:length(sub_nets), .combine = "rbind", .packages = c("igraph", "tidyverse")) %dopar%
        {

            # simulation for weak influence scenario
            sim_weak <- fabm(network = sub_nets[[i]]$network, rounds = rounds, params = list(s = 10,
                e = 10, w = 30, z = z1))$evo
            outcome_weak <- sim_weak$statistic[sim_weak$round == rounds & sim_weak$outcome == "follow_trend"]

            # simulation for strong influence scenario
            sim_strong <- fabm(network = sub_nets[[i]]$network, rounds = rounds, params = list(s = 10,
                e = 10, w = 30, z = z2))$evo
            outcome_strong <- sim_strong$statistic[sim_strong$round == rounds & sim_strong$outcome ==
                "follow_trend"]

            gc()

            # return results as a row to be rbinded in a results matrix
            return(c(outcome_weak, outcome_strong))
        }
})

stopCluster(cl)
closeAllConnections()


# and add these outcomes to our dataframe: df$outcome_weak <- outcome_weak df$outcome_strong <-
# outcome_strong

References

Lerman, Kristina, Xiaoran Yan, and Xin-Zeng Wu. 2016. “The "Majority Illusion" in Social Networks.” PloS One 11 (2): e0147617. https://doi.org/10.1371/journal.pone.0147617.
Newman, Mark. 2002. “Assortative Mixing in Networks.” Physical Review Letters 89 (20). https://doi.org/10.1103/PhysRevLett.89.208701.
LS0tDQp0aXRsZTogIkFCTSINCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCmxpbmstY2l0YXRpb25zOiB0cnVlDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkLSVtLSVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjc3M6IHR3ZWFrcy5jc3MNCiAgICB0b2M6ICB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgdG9jX2RlcHRoOiA0DQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogeWVzDQotLS0NCg0KYGBge3IsIGdsb2JhbHNldHRpbmdzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLmN1dG9mZj0xMDApLHRpZHk9VFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsY29tbWVudCA9ICIjPiIsIGNhY2hlPVRSVUUsIGNsYXNzLnNvdXJjZT1jKCJ0ZXN0IiksIGNsYXNzLm91dHB1dD1jKCJ0ZXN0MyIpKQ0Kb3B0aW9ucyh3aWR0aCA9IDEwMCkNCnJnbDo6c2V0dXBLbml0cigpDQoNCmNvbG9yaXplIDwtIGZ1bmN0aW9uKHgsIGNvbG9yKSB7c3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLCB4KSB9DQpgYGANCg0KDQpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0NCmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KLS0tICANCg0KIyBHZXR0aW5nIHN0YXJ0ZWQNCg0KVG8gY29weSB0aGUgY29kZSwgY2xpY2sgdGhlIGJ1dHRvbiBpbiB0aGUgdXBwZXIgcmlnaHQgY29ybmVyIG9mIHRoZSBjb2RlLWNodW5rcy4NCg0KIyMgY2xlYW4gdXANCg0KYGBge3IsIGNsZWFuX3VwLCByZXN1bHRzPSdoaWRlJ30NCnJtKGxpc3Q9bHMoKSkNCmdjKCkNCmBgYA0KDQo8YnI+DQoNCiMjIGdlbmVyYWwgY3VzdG9tIGZ1bmN0aW9ucw0KDQotIGBmcGFja2FnZS5jaGVja2A6IENoZWNrIGlmIHBhY2thZ2VzIGFyZSBpbnN0YWxsZWQgKGFuZCBpbnN0YWxsIGlmIG5vdCkgaW4gUg0KLSBgZnNhdmVgOiBGdW5jdGlvbiB0byBzYXZlIGRhdGEgd2l0aCB0aW1lIHN0YW1wIGluIGNvcnJlY3QgZGlyZWN0b3J5DQotIGBmbG9hZGA6IExvYWQgUi1vYmplY3RzIHVuZGVyIG5ldyBuYW1lcw0KLSBgZnNob3dkZmA6IFByaW50IG9iamVjdHMgKHRpYmJsZSAvIGRhdGEuZnJhbWUpIG5pY2VseSBvbiBzY3JlZW4gaW4gLlJtZC4NCi0gYGZwbG90X2dyYXBoYDogdmlzdWFsaXplIHRoZSBuZXR3b3JrIHRvcG9sb2d5IGFuZCBhZ2VudCBwb3NpdGlvbnMgKGRlZ3JlZS10cmFpdCBjb3JyZWxhdGlvbikNCg0KYGBge3IsIGN1c3RvbV9mdW5jdGlvbnMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KZnBhY2thZ2UuY2hlY2sgPC0gZnVuY3Rpb24ocGFja2FnZXMpIHsNCiAgICBsYXBwbHkocGFja2FnZXMsIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsNCiAgICAgICAgICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgICAgICAgICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KICAgICAgICB9DQogICAgfSkNCn0NCg0KZnNhdmUgPC0gZnVuY3Rpb24oeCwgZmlsZSwgbG9jYXRpb24gPSAiLi9kYXRhL3Byb2Nlc3NlZC8iLCAuLi4pIHsNCiAgICBpZiAoIWRpci5leGlzdHMobG9jYXRpb24pKQ0KICAgICAgICBkaXIuY3JlYXRlKGxvY2F0aW9uKQ0KICAgIGRhdGVuYW1lIDwtIHN1YnN0cihnc3ViKCJbOi1dIiwgIiIsIFN5cy50aW1lKCkpLCAxLCA4KQ0KICAgIHRvdGFsbmFtZSA8LSBwYXN0ZShsb2NhdGlvbiwgZGF0ZW5hbWUsIGZpbGUsIHNlcCA9ICIiKQ0KICAgIHByaW50KHBhc3RlKCJTQVZFRDogIiwgdG90YWxuYW1lLCBzZXAgPSAiIikpDQogICAgc2F2ZSh4LCBmaWxlID0gdG90YWxuYW1lKQ0KfQ0KDQpmbG9hZCA8LSBmdW5jdGlvbihmaWxlTmFtZSkgew0KICAgIGxvYWQoZmlsZU5hbWUpDQogICAgZ2V0KGxzKClbbHMoKSAhPSAiZmlsZU5hbWUiXSkNCn0NCg0KZnNob3dkZiA8LSBmdW5jdGlvbih4LCAuLi4pIHsNCiAgICBrbml0cjo6a2FibGUoeCwgZGlnaXRzID0gMiwgImh0bWwiLCAuLi4pICU+JQ0KICAgICAgICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKSAlPiUNCiAgICAgICAga2FibGVFeHRyYTo6c2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjMwMHB4IikNCn0NCg0KZnBsb3RfZ3JhcGggPC0gZnVuY3Rpb24oZ3JhcGgsIG1haW49TlVMTCwgbGF5b3V0X2FsZ289TlVMTCwgDQogICAgICAgICAgICAgICAgICAgICAgICBjb2wxID0gIiNGRkQ3MDAiLCBjb2wyID0gIiM4MDAwODAiKSB7DQogIHBsb3QoZ3JhcGgsDQogICAgICAgbWFpbiA9IG1haW4sDQogICAgICAgbGF5b3V0ID0gbGF5b3V0X2FsZ28sDQogICAgICAgdmVydGV4LmxhYmVsID0gTkEsDQogICAgICAgdmVydGV4LnNpemUgPSBkZWdyZWUoZ3JhcGgpICsgMiwgIyBub2RlIHNpemUgYmFzZWQgb24gZGVncmVlDQogICAgICAgdmVydGV4LmNvbG9yID0gaWZlbHNlKFYoZ3JhcGgpJHJvbGUgPT0gInRyZW5kc2V0dGVyIiwgY29sMSwgY29sMiksDQogICAgICAgZWRnZS53aWR0aCA9IDAuNSwNCiAgICAgICBlZGdlLmNvbG9yID0gImRhcmtncmV5IikNCiAgI2FkZCBsZWdlbmQNCiAgbGVnZW5kKCJib3R0b21sZWZ0IiwNCiAgICAgICAgIGxlZ2VuZCA9IGMoIlRyZW5kc2V0dGVyIiwgIkNvbmZvcm1pc3QiKSwNCiAgICAgICAgIHBjaCA9IDIxLA0KICAgICAgICAgY29sID0gYyhjb2wxLGNvbDIpLA0KICAgICAgICAgcHQuYmcgPSBjKGNvbDEsY29sMiksDQogICAgICAgICBwdC5jZXggPSAzLA0KICAgICAgICAgYnR5ID0gIm4iKQ0KfQ0KYGBgDQoNCjxicj4NCg0KIyMgbmVjZXNzYXJ5IHBhY2thZ2VzDQoNCi0gYHRpZHl2ZXJzZWANCi0gYHBsb3RseWANCi0gYGlncmFwaGANCi0gYHZpcGxvdGANCi0gYGdncGxvdDJgDQotIGBnZ3B1YnJgDQotIGBwbG90bHlgDQotIGBwYXJhbGxlbGANCi0gYGZvcmVhY2hgDQotIGBkb1BhcmFsbGVsYA0KDQpgYGB7ciwgcGFja2FnZXN9DQpwYWNrYWdlcyA9IGMoInRpZHl2ZXJzZSIsICJpZ3JhcGgiLCAidmlvcGxvdCIsICJnZ3Bsb3QyIiwgImdncHViciIsICJwbG90bHkiLCAicGFyYWxsZWwiLCAiZm9yZWFjaCIsICJkb1BhcmFsbGVsIikNCmludmlzaWJsZShmcGFja2FnZS5jaGVjayhwYWNrYWdlcykpDQpybShwYWNrYWdlcykNCmBgYA0KDQoNCi0tLQ0KDQojIHV0aWxpdHkgZnVuY3Rpb24NCg0KQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIHBheW9mZiBvZiBhIHNwZWNpZmljIGNob2ljZSwgZm9yIGFuIGFnZW50IChpZCksIGdpdmVuIHRoZSBhZ2VudHMnIGN1cnJlbnQgc3RhdHVzZXMsIHRoZSBuZXR3b3JrIHN0cnVjdHVyZSwgYW5kIHRoZSB1dGlsaXR5IGZ1bmN0aW9uIHBhcmFtZXRlcnM6DQoNCmBgYHtyLCB1dGlsaXR5X2Z1bmN0aW9ufQ0KZnV0aWxpdHkgPC0gZnVuY3Rpb24oYWdlbnRfaWQsIGNob2ljZSwgYWdlbnRzLCBuZXR3b3JrLCBwYXJhbXMpIHsNCiAgIyBnZXQgZWdvIGFuZCBoaXMgbG9jYWwgbmVpZ2hib3Job29kDQogIGVnbyA8LSBhZ2VudHNbYWdlbnRfaWQsIF0NCiAgbmVpZ2hib3JzIDwtIG5laWdoYm9ycyhuZXR3b3JrLCBlZ28kaWQpDQogIGFsdGVycyA8LSBhZ2VudHNbYXMubnVtZXJpYyhuZWlnaGJvcnMpLCBdDQogIG4gPC0gbnJvdyhhbHRlcnMpDQoNCiAgIyBjb3VudCBudW1iZXIgb2YgbmVpZ2hib3JzIHdobyBkaWQgKG5vdCkgZm9sbG93IHRoZSB0cmVuZA0KICBuMSA8LSBzdW0oYWx0ZXJzJGNob2ljZSA9PSAxKQ0KICBuMCA8LSBzdW0oYWx0ZXJzJGNob2ljZSA9PSAwKQ0KICANCiAgIyBjYWxjdWxhdGUgZXhwZWN0ZWQgdXRpbGl0eSAoZGVwZW5kaW5nIG9uIGFsdGVycycgY2hvaWNlcyBpbiBwcmlvciByb3VuZCkNCiAgaWYoZWdvJHJvbGUgPT0gImNvbmZvcm1pc3QiKSB7DQogICAgY2hvaWNlX3BheW9mZiA8LSBpZmVsc2UoY2hvaWNlID09IDAsIHBhcmFtcyRzLCAwKQ0KICAgIGNvb3JkaW5hdGlvbl9wYXlvZmYgPC0gaWZlbHNlKGNob2ljZSA9PSAwLCAjIG5vcm1hbGl6ZWQgYnkgbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChwYXJhbXMkdy9uKSAqIG4wLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChwYXJhbXMkei9uKSAqIG4xKQ0KICB9IGVsc2Ugew0KICAgIGNob2ljZV9wYXlvZmYgPC0gaWZlbHNlKGNob2ljZSA9PSAxLCBwYXJhbXMkZSwgMCkNCiAgICBjb29yZGluYXRpb25fcGF5b2ZmIDwtIDANCiAgfQ0KICByZXR1cm4obGlzdCh1dGlsaXR5ID0gY2hvaWNlX3BheW9mZiArIGNvb3JkaW5hdGlvbl9wYXlvZmYsDQogICAgICAgICAgICAgIG4xID0gbjEsIG4wID0gbjApKQ0KfQ0KYGBgDQoNCg0KLS0tLQ0KDQojIG5ldHdvcmsgc3RydWN0dXJlDQoNCiMjIGNvbmZpZ3VyYXRpb24gbW9kZWwNCg0KQSBmdW5jdGlvbiB0byBnZW5lcmF0ZSBhIGRlZ3JlZSBzZXF1ZW5jZSBiYXNlZCBvbiBhIHBvd2VyLWxhdyAvIGxvZy1ub3JtYWwgZGlzdHJpYnV0aW9uOg0KDQpgYGB7ciwgY29uZmlndXJhdGlvbl9tb2RlbH0NCmZkZWdzZXEgPC0gZnVuY3Rpb24obiwgZGlzdCA9ICJwb3dlci1sYXciLCBhbHBoYSwga19taW4gPSAxLCBrX21heCA9IG4gLSAxLCBzZWVkID0gTlVMTCkgew0KICANCiAgaWYgKCFpcy5udWxsKHNlZWQpKSB7DQogICAgc2V0LnNlZWQoc2VlZCkNCiAgfQ0KICANCiAgIyBnZW5lcmF0ZSBhIGRlZ3JlZSBzZXF1ZW5jZSBiYXNlZCBvbiBhIHBvd2VyLWxhdyBkaXN0cmlidXRpb24gb2YgdGhlIGZvcm0gcChrKSDiiJ0ga157LWFscGhhfQ0KICANCiAgIyBjcmVhdGUgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uDQogIHByb2JzIDwtICgxIC8gKGtfbWluOmtfbWF4KSleYWxwaGENCiAgICANCiAgIyBub3JtYWxpemUgcHJvYmFiaWxpdGllcw0KICBwcm9icyA8LSBwcm9icyAvIHN1bShwcm9icykgDQogIA0KICAjIHNhbXBsZSBhIGRlZ3JlZSBzZXF1ZW5jZQ0KICBkZWdzZXEgPC0gc2FtcGxlKGtfbWluOmtfbWF4LCBzaXplID0gbiwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSAoMS8oa19taW46a19tYXgpKV5hbHBoYSkNCiANCiAgIyBjb3JyZWN0IHRoZSBkZWdyZWUgc2VxdWVuY2UgaWYgaXRzIHN1bSBpcyBvZGQgKG5lY2Vzc2FyeSBmb3IgdGhlIGNvbmZpZ3VyYXRpb24gbW9kZWwpDQogIGlmIChzdW0oZGVnc2VxKSAlJSAyICE9IDApIHsNCiAgICBkZWdzZXFbMV0gPC0gZGVnc2VxWzFdICsgMQ0KICB9DQogIA0KICBpZiAoZGlzdCA9PSAicG93ZXItbGF3Iikgew0KICAgICMgaWYgdGhlIHNwZWNpZmllZCBkaXN0cmlidXRpb24gdHlwZSBpcyBwb3dlci1sYXcsIHJldHVybiB0aGUgZGVncmVlIHNlcXVlbmNlDQogICAgcmV0dXJuKGRlZ3NlcSkNCiAgfSBlbHNlIGlmIChkaXN0ID09ICJsb2ctbm9ybWFsIikgew0KICAgICMgaWYgdGhlIHNwZWNpZmllZCBkaXN0cmlidXRpb24gdHlwZSBpcyBsb2ctbm9ybWFsLCBnZW5lcmF0ZSBhIGRlZ3JlZSBzZXF1ZW5jZSBmb2xsb3dpbmcgdGhpcyBkaXN0cmlidXRpb247DQogICAgIyBidXQgd2l0aCBhIHNhbWUgbWVhbiBkZWdyZWUgPGs+IGFzIGl0cyAnc2NhbGUtZnJlZScgY291bnRlcnBhcnQuIA0KICAgIA0KICAgICMgY2FsY3VsYXRlIG1lYW4gZGVncmVlIGluIHNlcXVlbmNlDQogICAgbWVhbl9kZWcgPC0gbWVhbihkZWdzZXEpDQogICAgDQogICAgIyBnZW5lcmF0ZSByYXcgbG9nLW5vcm1hbCBkZWdyZWUgc2VxdWVuY2UNCiAgICByYXdfZGVnc2VxIDwtIHJsbm9ybShuID0gbiwgbWVhbmxvZyA9IGxvZyhtZWFuX2RlZyksIHNkbG9nID0gMSkNCiAgICANCiAgICAjIHJlLXNjYWxlIHRoZSBkZWdyZWUgc2VxdWVuY2UgdG8gbWF0Y2ggdGhlIHRhcmdldCBtZWFuIGRlZ3JlZQ0KICAgIHNjYWxpbmdfZmN0ciA8LSBtZWFuX2RlZyAvIG1lYW4ocmF3X2RlZ3NlcSkNCiAgICBzY2FsZWRfZGVnc2VxIDwtIHJhd19kZWdzZXEgKiBzY2FsaW5nX2ZjdHINCiAgICANCiAgICAjIGFwcGx5IGJvdW5kcyBba19taW4sIGtfbWF4XSBhbmQgcm91bmQgdG8gaW50ZWdlciB2YWx1ZXMNCiAgICBib3VuZGVkX2RlZ3NlcSA8LSBwbWluKHBtYXgocm91bmQoc2NhbGVkX2RlZ3NlcSksIGtfbWluKSwga19tYXgpDQogICAgDQogICAgIyBzaW5jZSB0aGUgbWVhbiBtYXkgc2hpZnQgYWZ0ZXIgcm91bmRpbmcgYW5kIGJvdW5kaW5nLCByZS1zY2FsZSBhZ2FpbjoNCiAgICBzY2FsaW5nX2ZjdHIyIDwtIG1lYW5fZGVnIC8gbWVhbihib3VuZGVkX2RlZ3NlcSkNCiAgICBkZWdzZXEgPC0gcG1pbihwbWF4KHJvdW5kKGJvdW5kZWRfZGVnc2VxICogc2NhbGluZ19mY3RyMiksIGtfbWluKSwga19tYXgpDQogICAgDQogICAgIyBjb3JyZWN0IHRoZSBkZWdyZWUgc2VxdWVuY2UgaWYgaXRzIHN1bSBpcyBvZGQgKG5lY2Vzc2FyeSBmb3IgdGhlIGNvbmZpZ3VyYXRpb24gbW9kZWwpDQogICAgaWYgKHN1bShkZWdzZXEpICUlIDIgIT0gMCkgew0KICAgICAgZGVnc2VxWzFdIDwtIGRlZ3NlcVsxXSArIDENCiAgICB9DQogICAgcmV0dXJuKGRlZ3NlcSkNCiAgfSBlbHNlIHsNCiAgICBzdG9wKCJJbnZhbGlkIGRpc3RyaWJ1dGlvbiB0eXBlLiBQbGVhc2UgY2hvb3NlICdwb3dlci1sYXcnIG9yICdsb2ctbm9ybWFsJy4iKQ0KICB9DQp9DQpgYGANCg0KYGBge3J9DQpuID0gOTYgIyBudW1iZXIgb2YgYWdlbnRzDQpwX3QgPSAwLjEgIyBwcm9wb3J0aW9uICJ0cmVuZHNldHRlcnMiDQoNCiMgcGxvdHRpbmcgd2luZG93DQpwYXIobWZyb3c9YygxLDIpKQ0KDQpmb3IgKGkgaW4gYygicG93ZXItbGF3IiwgImxvZy1ub3JtYWwiKSkgew0KICAjIGdlbmVyYXRlIGRlZ3JlZSBzZXF1ZW5jZSB1c2luZyBhIHNlZWQgDQogIGRlZ3NlcSA8LSBmZGVnc2VxKG4gPSBuLCBkaXN0ID0gaSwgYWxwaGEgPSAyLjcsIGtfbWluID0gMywgc2VlZCA9IDEyMykNCiAgDQogICMgcmV0cmlldmUgPGs+DQogIG1lYW5fZGVnIDwtIG1lYW4oZGVnc2VxKQ0KICANCiAgIyB0byBncmFwaCBvYmplY3QsIGJhc2VkIG9uIHRoZSBWaWdlci1MYXBhdHkgYWxnb3JpdGhtDQogIG5ldHdvcmsgPC0gc2FtcGxlX2RlZ3NlcShkZWdzZXEsIG1ldGhvZCA9ICJ2bCIpDQogDQogICMgcmFuZG9tbHkgYXNzaWduIHJvbGVzIHRvIG5vZGVzLCBiYXNlZCBvbiBwcm9wb3J0aW9uIHRyZW5kc2V0dGVycw0KICBWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKGMocmVwKCJ0cmVuZHNldHRlciIsIGZsb29yKG4gKiBwX3QpKSwgcmVwKCJjb25mb3JtaXN0IiwgbiAtIGZsb29yKG4gKiBwX3QpKSkpDQogIA0KICAjIHBsb3QgdGhlIGdyYXBoDQogIGZwbG90X2dyYXBoKG5ldHdvcmssIG1haW4gPSBwYXN0ZShpLCAiZGVncmVlIGRpc3RyaWJ1dGlvblxuIiwgIjxrPj0iLCByb3VuZChtZWFuX2RlZywgMikpKQ0KfQ0KYGBgDQoNCiMjIGVyZG9zLXJlbnlpIHJhbmRvbSBncmFwaA0KDQpQcm9kdWNlIHJhbmRvbSBuZXR3b3JrcyB3aXRoIHRoZSBzYW1lIGF2ZXJhZ2UgZGVncmVlICgmbHQ7ayZndDspIGFzIHNjYWxlLWZyZWUgY291bnRlcnBhcnRzIChieSB0d2Vha2luZyBlZGdlIHByb2JhYmlsaXR5IHApOg0KDQotICZsdDtrJmd0OyA9IHAgKiAobiAtIDEpDQotIHAgPSAmbHQ7ayZndDsgLyAobiAtIDEpDQoNCg0KYGBge3J9DQojIGxvb3Agb3ZlciBhIGhpZ2hlciBudW1iZXIgb2Ygc2ltdWxhdGVkIGNvbmZpZ3VyYXRpb24gbW9kZWwgdG8gZ2V0IGFuIGFjY3VyYXRlIGVzdGltYXRlIG9mIGF2ZXJhZ2UgZGVncmVlIDxrPiBpbiBzY2FsZS1mcmVlIG5ldHdvcmtzDQpuSXRlciA8LSAxZTMNCmFscGhhcyA8LSBjKDIuNCwgMi43LCAzKSAgDQprbWVhbiA8LSBsaXN0KCkgDQoNCmZvciAoYWxwaGEgaW4gYWxwaGFzKSB7DQogIGttZWFuW1thcy5jaGFyYWN0ZXIoYWxwaGEpXV0gPC0gbnVtZXJpYyhuSXRlcikNCiAgZm9yIChpIGluIDE6bkl0ZXIpIHsNCiAgICB0cnkoew0KICAgICAgZGVnc2VxIDwtIGZkZWdzZXEobiA9IDk2LCBhbHBoYSA9IGFscGhhLCBrX21pbiA9IDMpICANCiAgICAgIG5ldHdvcmsgPC0gc2FtcGxlX2RlZ3NlcShkZWdzZXEsIG1ldGhvZCA9ICJ2bCIpICANCiAgICAgIGttZWFuW1thcy5jaGFyYWN0ZXIoYWxwaGEpXV1baV0gPC0gbWVhbihkZWdyZWUobmV0d29yaykpIA0KICAgIH0sIHNpbGVudCA9IFRSVUUpIA0KICB9DQp9DQoNCnBsaXN0IDwtIGxpc3QoDQogIG1lYW4oa21lYW4kYDIuNGBba21lYW4kYDIuNGAhPTBdKSAvIChuLTEpLA0KICBtZWFuKGttZWFuJGAyLjdgW2ttZWFuJGAyLjdgIT0wXSkgLyAobi0xKSwNCiAgbWVhbihrbWVhbiRgM2Bba21lYW4kYDNgIT0wXSkgLyAobi0xKQ0KKQ0KDQpwYXIobWZyb3c9YygxLCBsZW5ndGgocGxpc3QpKSwgbWFyPWMoMSwxLDMsMSkpDQpmb3IgKGkgaW4gMTpsZW5ndGgocGxpc3QpKSB7DQogIG5ldHdvcmsgPC0gZXJkb3MucmVueWkuZ2FtZSg5NiwgcD1wbGlzdFtbaV1dKQ0KICBWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKGMocmVwKCJ0cmVuZHNldHRlciIsIGZsb29yKG4gKiBwX3QpKSwgcmVwKCJjb25mb3JtaXN0IiwgbiAtIGZsb29yKG4gKiBwX3QpKSkpDQogIGZwbG90X2dyYXBoKG5ldHdvcmssIG1haW4gPSBwYXN0ZSgicmFuZG9tIG5ldHdvcmtcbiIsICJwID0iLCAgcm91bmQocGxpc3RbW2ldXSwgMykpKSAgDQp9DQpgYGANCg0KIyMgc21hbGwtd29ybGQNCg0KYGBge3J9DQpuZXR3b3JrIDwtIHNhbXBsZV9zbWFsbHdvcmxkKGRpbSA9IDEsIHNpemUgPSA5NiwgbmVpID0gMywgcCA9IDAuMTApDQpWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKGMocmVwKCJ0cmVuZHNldHRlciIsIGZsb29yKG4qcF90KSksIHJlcCgiY29uZm9ybWlzdCIsIG4gLSBmbG9vcihuKnBfdCkpKSkNCmZwbG90X2dyYXBoKG5ldHdvcmssIG1haW4gPSAic21hbGwtd29ybGQgbmV0d29yayIpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0zfQ0KI3Bsb3QgZGVncmVlIGRpc3RyaWJ1dGlvbnMgc2lkZS1ieS1zaWRlIA0KZXIgPC0gZXJkb3MucmVueWkuZ2FtZShuLCBwPTAuMDEpDQpzdyA8LSBzYW1wbGVfc21hbGx3b3JsZChkaW0gPSAxLCBzaXplID0gbiwgbmVpID0gMywgcCA9IDAuMTApDQpzZiA8LSBzYW1wbGVfZGVnc2VxKGZkZWdzZXEobj1uLCBhbHBoYT0yLjcsIGtfbWluPTMpLCBtZXRob2QgPSAidmwiKQ0KbG4gPC0gc2FtcGxlX2RlZ3NlcShmZGVnc2VxKG49biwgYWxwaGE9Mi43LCBkaXN0ID0gImxvZy1ub3JtYWwiLCBrX21pbj0zKSwgbWV0aG9kID0gInZsIikNCg0KcGFyKG1mcm93PWMoMSw0KSkNCnBsb3QoZGVncmVlX2Rpc3RyaWJ1dGlvbihlcikscGNoPTIwLHhsYWI9ImsiLHlsYWI9IlAoaykiLGxhcz0xLG1haW49IlJhbmRvbSBncmFwaCIsbG9nPSJ4eSIpDQpwbG90KGRlZ3JlZV9kaXN0cmlidXRpb24oc3cpLHBjaD0yMCx4bGFiPSJrIix5bGFiPSJQKGspIixsYXM9MSxtYWluPSJTbWFsbC13b3JsZCBncmFwaCIsbG9nPSJ4eSIpDQpwbG90KGRlZ3JlZV9kaXN0cmlidXRpb24oc2YpLHBjaD0yMCx4bGFiPSJrIix5bGFiPSJQKGspIixsYXM9MSxtYWluPSJTY2FsZS1mcmVlIGdyYXBoIixsb2c9Inh5IikNCnBsb3QoZGVncmVlX2Rpc3RyaWJ1dGlvbihsbikscGNoPTIwLHhsYWI9ImsiLHlsYWI9IlAoaykiLGxhcz0xLG1haW49IkxvZy1ub3JtYWwgZGlzdHJpYnV0aW9uIixsb2c9Inh5IikNCmBgYA0KDQotLS0NCg0KDQojIyByZXdpcmluZyBhbGdvcml0aG0NCg0KUmV3aXJpbmcgYWxnb3JpdGhtIHRvIHR1bmUgZGVncmVlIChkaXMpYXNzb3J0YXRpdml0eSBbQE5ld21hbjIwMDJdOg0KDQpgYGB7cn0NCmZyZXdpcmVfciA8LSBmdW5jdGlvbihuZXR3b3JrLCB0YXJnZXRfciwgbWF4X2l0ZXIgPSAxZTUsIHRvbCA9IDAuMDEsIHZlcmJvc2UgPSBUUlVFKSB7DQogIA0KICBjdXJyZW50X3IgPC0gYXNzb3J0YXRpdml0eV9kZWdyZWUobmV0d29yaykNCiAgaXRlcmF0aW9uIDwtIDENCiAgDQogIGlmICh2ZXJib3NlKSB7DQogICAgY2F0KCJUYXJnZXQgYXNzb3J0YXRpdml0eSBjb2VmZmljaWVudDoiLCB0YXJnZXRfciwgIlxuIikNCiAgICBjYXQoIlN0YXJ0aW5nIGFzc29ydGF0aXZpdHkgY29lZmZpY2llbnQ6IiwgY3VycmVudF9yLCAiXG4iKQ0KICAgIGNhdCgiVG9sZXJhbmNlOiIsIHRvbCwgIlxuIikNCiAgfQ0KICANCiAgd2hpbGUgKGFicyhjdXJyZW50X3IgLSB0YXJnZXRfcikgPiB0b2wgJiYgaXRlcmF0aW9uIDwgbWF4X2l0ZXIpIHsNCiAgICANCiAgICAjIGdldCBuZXR3b3JrIGVkZ2VzDQogICAgZWRnZXMgPC0gRShuZXR3b3JrKQ0KICAgICMgdG8gZWRnZWxpc3QNCiAgICBlZGdlX2xpc3QgPC0gZW5kcyhuZXR3b3JrLCBlZGdlcykNCiAgICANCiAgICAjIHJhbmRvbWx5IHNlbGVjdCB0d28gcGFpcnMgb2YgY29ubmVjdGVkIG5vZGVzDQogICAgaWR4MSA8LSBzYW1wbGUoMTpucm93KGVkZ2VfbGlzdCksIDEpDQogICAgaWR4MiA8LSBzYW1wbGUoMTpucm93KGVkZ2VfbGlzdCksIDEpDQogICAgDQogICAgIyBleHRyYWN0IG5vZGUgaW5kaWNlcw0KICAgIHUxIDwtIGVkZ2VfbGlzdFtpZHgxLCAxXSAjIG5vZGUgMSBvZiBmaXJzdCBlZGdlDQogICAgdjEgPC0gZWRnZV9saXN0W2lkeDEsIDJdICMgbm9kZSAyIG9mIGZpcnN0IGVkZ2UNCiAgICB1MiA8LSBlZGdlX2xpc3RbaWR4MiwgMV0gIyBldGMNCiAgICB2MiA8LSBlZGdlX2xpc3RbaWR4MiwgMl0gDQogICAgDQogICAgIyBjaGVjayBpZiB0aGUgdHdvIHBhaXJzIG9mIGNvbm5lY3RlZCBub2RlcyAodTEsIHYxOyB1MiwgdjIpIGFyZSBkaXNqb2ludA0KICAgIGlmIChsZW5ndGgodW5pcXVlKGModTEsIHYxLCB1MiwgdjIpKSkgPT0gNCkgew0KICAgICAgIyBjaGVjayBpZiB0aGVyZSBpcyBhbHJlYWR5IGFuIGVkZ2UgYWNyb3NzIHRoZSBub2RlLXBhaXJzDQogICAgICAjIGVuc3VyZSBubyBsb29wcyBhbmQgbm8gZHVwbGljYXRlIGVkZ2VzDQogICAgICBpZiAoIWFyZV9hZGphY2VudChuZXR3b3JrLCB1MSwgdTIpICYmICFhcmVfYWRqYWNlbnQobmV0d29yaywgdjEsIHYyKSAmJiB1MSAhPSB2MiAmJiB1MiAhPSB2MSkgew0KICAgICAgICANCiAgICAgICAgIyBwZXJmb3JtIHRoZSBlZGdlIHN3YXAgKHUxLHYxKSA8LT4gKHUyLHYyKSBiZWNvbWVzICh1MSx2MikgPC0+ICh1Mix2MSkNCiAgICAgICAgbmV3X25ldHdvcmsgPC0gbmV0d29yayAjIGNvcHkgbmV0d29yaw0KICAgICAgICANCiAgICAgICAgIyBjaGVjayBpZiB0aGUgbmV3IGVkZ2VzIGFscmVhZHkgZXhpc3QgdG8gYXZvaWQgZHVwbGljYXRlcw0KICAgICAgICBpZiAoIWFyZV9hZGphY2VudChuZXdfbmV0d29yaywgdTEsIHYyKSAmJiAhYXJlX2FkamFjZW50KG5ld19uZXR3b3JrLCB1MiwgdjEpKSB7DQogICAgICAgICAgIyBhZGQgZWRnZXMNCiAgICAgICAgICBuZXdfbmV0d29yayA8LSBhZGRfZWRnZXMobmV3X25ldHdvcmssIGModTEsIHYyLCB1MiwgdjEpKQ0KICAgICAgICAgICMgcmVtb3ZlIGVkZ2VzDQogICAgICAgICAgbmV3X25ldHdvcmsgPC0gZGVsZXRlX2VkZ2VzKG5ld19uZXR3b3JrLCBnZXQuZWRnZS5pZHMobmV3X25ldHdvcmssIGModTEsIHYxLCB1MiwgdjIpKSkNCiAgICAgICAgICANCiAgICAgICAgICAjIG5ldyBhc3NvcnRhdGl2aXR5DQogICAgICAgICAgbmV3X3IgPC0gYXNzb3J0YXRpdml0eV9kZWdyZWUobmV3X25ldHdvcmspDQogICAgICAgICAgDQogICAgICAgICAgIyBhY2NlcHQgdGllIHN3YXAgaWYgaXQgYnJpbmdzIHVzIGNsb3NlciB0byB0aGUgdGFyZ2V0IGFzc29ydGF0aXZpdHkNCiAgICAgICAgICBpZiAoYWJzKG5ld19yIC0gdGFyZ2V0X3IpIDwgYWJzKGN1cnJlbnRfciAtIHRhcmdldF9yKSkgew0KICAgICAgICAgICAgY3VycmVudF9yIDwtIG5ld19yDQogICAgICAgICAgICBuZXR3b3JrIDwtIG5ld19uZXR3b3JrDQogICAgICAgICAgICBpZiAodmVyYm9zZSkgeyANCiAgICAgICAgICAgICAgY2F0KCJSZXdpcmluZyBhdCBpdGVyYXRpb24iLCBpdGVyYXRpb24sICJicm91Z2h0IGFzc29ydGF0aXZpdHkgY2xvc2VyIHRvIHRhcmdldCEgQ3VycmVudCBhc3NvcnRhdGl2aXR5IGNvZWZmaWNpZW50OiIsIG5ld19yLCAiXG4iKQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0NCiAgICBpdGVyYXRpb24gPC0gaXRlcmF0aW9uICsgMQ0KICB9DQogIA0KICBpZiAodmVyYm9zZSkgew0KICAgIGNhdCgiRmluYWwgYXNzb3J0YXRpdml0eSBjb2VmZmljaWVudDoiLCBjdXJyZW50X3IsICJcbiIpDQogICAgaWYgKGFicyhjdXJyZW50X3IgLSB0YXJnZXRfcikgPD0gdG9sKSB7DQogICAgICBjYXQoIlRhcmdldCByZWFjaGVkIHdpdGhpbiB0b2xlcmFuY2UuXG4iKQ0KICAgIH0gZWxzZSB7DQogICAgICBjYXQoIlJlYWNoZWQgbWF4aW11bSBpdGVyYXRpb25zIHdpdGhvdXQgbWVldGluZyB0YXJnZXQuXG4iKQ0KICAgIH0NCiAgfQ0KICANCiAgcmV0dXJuKG5ldHdvcmspDQp9DQpgYGANCg0KPGJyPg0KDQpBcHBseSB0aGUgZnVuY3Rpb246DQoNCmBgYHtyfQ0KI2luaXRpYWxpemUgc2NhbGUtZnJlZSBuZXR3b3JrDQpuZXR3b3JrIDwtIHNhbXBsZV9kZWdzZXEoZmRlZ3NlcShuID0gbiwgYWxwaGEgPSAyLjcsIGtfbWluID0gMywgc2VlZCA9IDEyMyksIG1ldGhvZCA9ICJ2bCIpDQpWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKGMocmVwKCJ0cmVuZHNldHRlciIsIGZsb29yKG4qcF90KSksIHJlcCgiY29uZm9ybWlzdCIsIG4gLSBmbG9vcihuKnBfdCkpKSkNCg0KI2dldCBjdXJyZW50IGFzc29ydGF0aXZpdHkgY29lZmZpY2llbnQNCmFzc29ydGF0aXZpdHlfZGVncmVlKG5ldHdvcmspDQoNCiNzZXQgbmV3IHRhcmdldHMNCmZyZXdpcmVfcihuZXR3b3JrLCB0YXJnZXRfciA9IC0wLjIsIG1heF9pdGVyID0gMWUzKQ0KYGBgDQoNCi0tLQ0KDQojIyBzd2FwcGluZyBhbGdvcml0aG0NCg0KU3dhcHBpbmcgYWxnb3JpdGhtIHRvIHR1bmUgZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uIFtATGVybWFuMjAxNl06DQoNCg0KYGBge3J9DQojIHdlIG1hbmlwdWxhdGUgZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uIChyaG8pIGJ5IHN3YXBwaW5nIGF0dHJpYnV0ZSB2YWx1ZXMgYW1vbmcgdGhlIG5vZGVzLiBUbyBpbmNyZWFzZSBccmhvX3treH0sIHdlIHJhbmRvbWx5IGNob29zZSBub2RlcyB2XzEgd2l0aCB4PTEgYW5kIHZfMCB3aXRoIHg9MCBhbmQgc3dhcCB0aGVpciBhdHRyaWJ1dGVzIGlmIHRoZSBkZWdyZWUgb2YgdGhlIGRlZ3JlZSBvZiB2XzAgaXMgZ3JlYXRlciB0aGFuIHRoYXQgb2Ygdl8xICh1bnRpbCB0aGUgZGVzaXJlZCBccmhvX3treH0gaXMgcmVhY2hlZDsgb3IgaXQgbm8gbG9uZ2VyIGNoYW5nZXMpLg0KZmRlZ3RyYWl0Y29yIDwtIGZ1bmN0aW9uKG5ldHdvcmspIHsNCiAgcm9sZXMgPC0gaWZlbHNlKFYobmV0d29yaykkcm9sZSA9PSAidHJlbmRzZXR0ZXIiLCAxLCAwKQ0KICBkZWdyZWVzIDwtIGRlZ3JlZShuZXR3b3JrKQ0KICByZXR1cm4obGlzdChjb3IgPSBjb3Iocm9sZXMsIGRlZ3JlZXMpLCByb2xlcyA9IHJvbGVzLCBkZWdyZWVzID0gZGVncmVlcykpDQp9DQoNCiNzd2FwcGluZyBmdW5jdGlvbiB0byBhZGp1c3QgZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uDQpmc3dhcF9yaG8gPC0gZnVuY3Rpb24obmV0d29yaywgdGFyZ2V0X3JobywgbWF4X2l0ZXIgPSAxZTMsIHRvbCA9IDAuMDUsIHZlcmJvc2UgPSBUUlVFKSB7DQogIA0KICBjdXJyZW50IDwtIGZkZWd0cmFpdGNvcihuZXR3b3JrKQ0KICBpdGVyYXRpb24gPC0gMQ0KICBiZXN0X25ldHdvcmsgPC0gbmV0d29yaw0KICBiZXN0X3JobyA8LSBjdXJyZW50JGNvcg0KICANCiAgaWYgKHZlcmJvc2UpIHsNCiAgICBjYXQoIlRhcmdldCBkZWdyZWUtdHJhaXQgY29ycmVsYXRpb246IiwgdGFyZ2V0X3JobywgIlxuIikNCiAgICBjYXQoIlN0YXJ0aW5nIGRlZ3JlZS10cmFpdCBjb3JyZWxhdGlvbjoiLCBjdXJyZW50JGNvciwgIlxuIikNCiAgICBjYXQoIlRvbGVyYW5jZToiLCB0b2wsICJcblxuIikNCiAgfQ0KICANCiAgd2hpbGUgKGl0ZXJhdGlvbiA8PSBtYXhfaXRlcikgew0KICAgICMgY2hlY2sgaWYgd2UgYXJlIGFscmVhZHkgd2l0aGluIHRvbGVyYW5jZQ0KICAgIGlmIChhYnMoY3VycmVudCRjb3IgLSB0YXJnZXRfcmhvKSA8PSB0b2wpIHsNCiAgICAgIGlmICh2ZXJib3NlKSBjYXQoIlRhcmdldCByZWFjaGVkIHdpdGhpbiB0b2xlcmFuY2UgYXQgaXRlcmF0aW9uIiwgaXRlcmF0aW9uLCAiLlxuIikNCiAgICAgIGJyZWFrDQogICAgfQ0KICAgIA0KICAgICMgcmFuZG9tbHkgc2VsZWN0IG5vZGVzIGZvciBzd2FwcGluZw0KICAgIHYxIDwtIHNhbXBsZSh3aGljaChjdXJyZW50JHJvbGVzID09IDEpLCAxKQ0KICAgIHYwIDwtIHNhbXBsZSh3aGljaChjdXJyZW50JHJvbGVzID09IDApLCAxKQ0KICAgIA0KICAgICMgZ2V0IGRlZ3JlZXMgb2Ygc2VsZWN0ZWQgbm9kZXMNCiAgICBrMSA8LSBjdXJyZW50JGRlZ3JlZXNbdjFdDQogICAgazAgPC0gY3VycmVudCRkZWdyZWVzW3YwXQ0KICAgIA0KICAgICMgc3dhcCByb2xlcyBpZiBjb25kaXRpb24ga192MCA+IGtfdjEgaXMgbWV0DQogICAgaWYgKGswID4gazEpIHsNCiAgICAgIGN1cnJlbnQkcm9sZXNbdjFdIDwtIDANCiAgICAgIGN1cnJlbnQkcm9sZXNbdjBdIDwtIDENCiAgICAgIA0KICAgICAgIyB1cGRhdGUgZ3JhcGggcm9sZXMNCiAgICAgIFYobmV0d29yaykkcm9sZSA8LSBpZmVsc2UoY3VycmVudCRyb2xlcyA9PSAxLCAidHJlbmRzZXR0ZXIiLCAiY29uZm9ybWlzdCIpDQogICAgICANCiAgICAgICMgcmVjYWxjdWxhdGUgZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uDQogICAgICBjdXJyZW50IDwtIGZkZWd0cmFpdGNvcihuZXR3b3JrKQ0KICAgICAgDQogICAgICAjIGNoZWNrIGlmIHRoaXMgaXMgdGhlIGNsb3Nlc3QgY29ycmVsYXRpb24gdG8gdGhlIHRhcmdldCBzbyBmYXINCiAgICAgIGlmIChhYnMoY3VycmVudCRjb3IgLSB0YXJnZXRfcmhvKSA8IGFicyhiZXN0X3JobyAtIHRhcmdldF9yaG8pKSB7DQogICAgICAgIGJlc3RfbmV0d29yayA8LSBuZXR3b3JrDQogICAgICAgIGJlc3RfcmhvIDwtIGN1cnJlbnQkY29yDQogICAgICAgIGlmICh2ZXJib3NlKSB7DQogICAgICAgICAgY2F0KCJUcmFpdC1zd2FwcGluZyBhdCBpdGVyYXRpb24iLCBpdGVyYXRpb24sICJicm91Z2h0IGNvcnJlbGF0aW9uIGNsb3NlciB0byB0YXJnZXQhIEN1cnJlbnQgY29ycmVsYXRpb246IiwgY3VycmVudCRjb3IsICJcbiIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9DQogICAgaXRlcmF0aW9uIDwtIGl0ZXJhdGlvbiArIDENCiAgfQ0KICANCiAgIyBjaGVjayBpZiB0aGUgZmluYWwgY29ycmVsYXRpb24gaXMgd29yc2UgdGhhbiB0aGUgYmVzdCBjb3JyZWxhdGlvbg0KICBmaW5hbF9jb3JyZWxhdGlvbiA8LSBjdXJyZW50JGNvcg0KICBpZiAoYWJzKGZpbmFsX2NvcnJlbGF0aW9uIC0gdGFyZ2V0X3JobykgPiBhYnMoYmVzdF9yaG8gLSB0YXJnZXRfcmhvKSkgew0KICAgIGlmICh2ZXJib3NlKSB7DQogICAgICBjYXQoIlxuV2FybmluZzogRmluYWwgaXRlcmF0aW9uIG1hZGUgdGhlIGNvcnJlbGF0aW9uIHdvcnNlLiBSZXZlcnRpbmcgdG8gYmVzdCBvYnNlcnZlZCBjb3JyZWxhdGlvbi5cbiIpDQogICAgfQ0KICB9DQogIA0KICBpZiAodmVyYm9zZSkgew0KICAgIGNhdCgiXG5GaW5hbCBkZWdyZWUtdHJhaXQgY29ycmVsYXRpb246IiwgYmVzdF9yaG8sICJcbiIpDQogICAgaWYgKGFicyhiZXN0X3JobyAtIHRhcmdldF9yaG8pIDw9IHRvbCkgew0KICAgICAgY2F0KCJUYXJnZXQgcmVhY2hlZCB3aXRoaW4gdG9sZXJhbmNlLlxuIikNCiAgICB9IGVsc2UgaWYgKGl0ZXJhdGlvbiA+IG1heF9pdGVyKSB7DQogICAgICBjYXQoIlJlYWNoZWQgbWF4aW11bSBpdGVyYXRpb25zIHdpdGhvdXQgbWVldGluZyB0YXJnZXQuXG4iKQ0KICAgIH0NCiAgfQ0KICByZXR1cm4oYmVzdF9uZXR3b3JrKQ0KfQ0KYGBgDQoNCjxicj4NCg0KQXBwbHkgdGhlIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCiNnZXQgY3VycmVudCBkZWdyZWUtdHJhaXQgY29ycmVsYXRpb24NCmZkZWd0cmFpdGNvcihuZXR3b3JrKQ0KDQojc2V0IG5ldyB0YXJnZXQNCnRhcmdldCA9IDAuNQ0KDQpmZGVndHJhaXRjb3IoZnN3YXBfcmhvKG5ldHdvcmsgPSBuZXR3b3JrLCB0YXJnZXRfcmhvID0gdGFyZ2V0LCB0b2w9MC4wMSkpDQpgYGANCg0KPGJyPg0KDQpTaW11bGF0ZSBuZXR3b3JrcyAoaGVyZSwgc2NhbGUtZnJlZSB3aXRoIGFscGhhID0gMi43KSB3aXRoIGluZGVwZW5kZW50bHkgdmFyeWluZyBkZWdyZWUgYXNzb3J0YXRpdml0eSBhbmQgZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uOg0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJ30NCnBhcihtZnJvdz1jKDMsMykpDQoNCiMgTkVUV09SS1MNCmFscGhhID0gMi43DQpkZWdzZXEgPC0gZmRlZ3NlcShuID0gOTYsIGFscGhhID0gYWxwaGEsIGtfbWluID0gMywgc2VlZCA9IDIzNTIpDQpuZXR3b3JrIDwtIHNhbXBsZV9kZWdzZXEoZGVnc2VxLCBtZXRob2QgPSAidmwiKQ0KVihuZXR3b3JrKSRyb2xlIDwtIHNhbXBsZShjKHJlcCgidHJlbmRzZXR0ZXIiLCBmbG9vcihuKnBfdCkpLCByZXAoImNvbmZvcm1pc3QiLCBuIC0gZmxvb3IobipwX3QpKSkpDQoNCm5ldHdvcmsxIDwtIGZyZXdpcmVfcihuZXR3b3JrLCB0YXJnZXRfciA9IC0wLjEsIHZlcmJvc2UgPSBGQUxTRSkNCg0KZnBsb3RfZ3JhcGgobmV0d29yazEsDQogICAgICAgICAgICBtYWluPXBhc3RlMCgiY29uZmlndXJhdGlvbiBtb2RlbFxuZGVncmVlIHNlcXVlbmNlIGdlbmVyYXRlZCBmcm9tIHAoayl+a157Lc6xfVxuTj05NjsgcHJvcC4gdHJlbmRzZXR0ZXIgPSAwLjE7IM6xPTIuNzsga19taW49M1xucl9rayA9ICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoYXNzb3J0YXRpdml0eV9kZWdyZWUobmV0d29yazEpLDMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICI7IHBfa3ggPSAiLCByb3VuZChmZGVndHJhaXRjb3IobmV0d29yazEpJGNvciwzKSkpDQoNCm5ldHdvcmsyIDwtIGZyZXdpcmVfcihuZXR3b3JrLCB0YXJnZXRfciA9IC0wLjMsIHZlcmJvc2UgPSBGQUxTRSkNCg0KZnBsb3RfZ3JhcGgobmV0d29yazIsDQogICAgICAgICAgICBtYWluPXBhc3RlMCgiY29uZmlndXJhdGlvbiBtb2RlbFxuZGVncmVlIHNlcXVlbmNlIGdlbmVyYXRlZCBmcm9tIHAoayl+a157Lc6xfVxuTj05NjsgcHJvcC4gdHJlbmRzZXR0ZXIgPSAwLjE7IM6xPTIuNzsga19taW49M1xucl9rayA9ICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoYXNzb3J0YXRpdml0eV9kZWdyZWUobmV0d29yazIpLDMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICI7IHBfa3ggPSAiLCByb3VuZChmZGVndHJhaXRjb3IobmV0d29yazIpJGNvciwzKSkpDQoNCm5ldHdvcmszIDwtIGZzd2FwX3JobyhuZXR3b3JrMiwgdGFyZ2V0X3JobyA9IDAuNSwgdmVyYm9zZT1GQUxTRSkNCg0KZnBsb3RfZ3JhcGgobmV0d29yazMsDQogICAgICAgICAgICBtYWluPXBhc3RlMCgiY29uZmlndXJhdGlvbiBtb2RlbFxuZGVncmVlIHNlcXVlbmNlIGdlbmVyYXRlZCBmcm9tIHAoayl+a157Lc6xfVxuTj05NjsgcHJvcC4gdHJlbmRzZXR0ZXIgPSAwLjE7IM6xPTIuNzsga19taW49M1xucl9rayA9ICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoYXNzb3J0YXRpdml0eV9kZWdyZWUobmV0d29yazMpLDMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICI7IHBfa3ggPSAiLCByb3VuZChmZGVndHJhaXRjb3IobmV0d29yazMpJGNvciwzKSkpDQoNCiMgREVHUkVFIEFTU09SVEFUSVZJVFkNCg0KcGxvdChkZWdyZWUobmV0d29yayksIGtubihuZXR3b3JrKSRrbm4sIHBjaD0xOSwgeGxhYiA9ICJOb2RlIGRlZ3JlZSAoaykiLCB5bGFiID0gIkF2ZXJhZ2UgbmVpZ2hib3IgZGVncmVlICg8ayc+KSIsDQogICAgIG1haW4gPSAiZGVncmVlIGFzc29ydGF0aXZpdHkiKQ0KcGxvdChkZWdyZWUobmV0d29yazIpLCBrbm4obmV0d29yazIpJGtubiwgcGNoPTE5LCB4bGFiID0gIk5vZGUgZGVncmVlIChrKSIsIHlsYWIgPSAiQXZlcmFnZSBuZWlnaGJvciBkZWdyZWUgKDxrJz4pIiwNCiAgICAgbWFpbiA9ICJkZWdyZWUgYXNzb3J0YXRpdml0eSIpDQpwbG90KGRlZ3JlZShuZXR3b3JrMyksIGtubihuZXR3b3JrMykka25uLCBwY2g9MTksICB4bGFiID0gIk5vZGUgZGVncmVlIChrKSIsIHlsYWIgPSAiQXZlcmFnZSBuZWlnaGJvciBkZWdyZWUgKDxrJz4pIiwNCiAgICAgbWFpbiA9ICJkZWdyZWUgYXNzb3J0YXRpdml0eSIpDQoNCiMgVklPTElOIFBMT1QgT0YgREVHUkVFIERJU1RSSUJVVElPTiBQRVIgUk9MRQ0KDQojIGV4dHJhY3Qgbm9kZSBkZWdyZWVzDQpkZWdyZWVzIDwtIGRlZ3JlZShuZXR3b3JrMSkNCnRyZW5kc2V0dGVyX2RlZ3JlZXMgPC0gZGVncmVlc1tWKG5ldHdvcmsxKSRyb2xlID09ICJ0cmVuZHNldHRlciJdDQpjb25mb3JtaXN0X2RlZ3JlZXMgPC0gZGVncmVlc1tWKG5ldHdvcmsxKSRyb2xlID09ICJjb25mb3JtaXN0Il0NCg0KdmlvcGxvdCh0cmVuZHNldHRlcl9kZWdyZWVzLCBjb25mb3JtaXN0X2RlZ3JlZXMsIA0KICAgICAgICBuYW1lcyA9IGMoIlRyZW5kc2V0dGVycyIsICJDb25mb3JtaXN0cyIpLA0KICAgICAgICBjb2wgPSBjKCIjRkZENzAwIiwgIiM4MDAwODAiKSwNCiAgICAgICAgbWFpbiA9ICJkZWdyZWUgZGlzdHJpYnV0aW9uIikNCg0KZGVncmVlczIgPC0gZGVncmVlKG5ldHdvcmsyKQ0KdHJlbmRzZXR0ZXJfZGVncmVlczIgPC0gZGVncmVlczJbVihuZXR3b3JrMikkcm9sZSA9PSAidHJlbmRzZXR0ZXIiXQ0KY29uZm9ybWlzdF9kZWdyZWVzMiA8LSBkZWdyZWVzMltWKG5ldHdvcmsyKSRyb2xlID09ICJjb25mb3JtaXN0Il0NCg0KdmlvcGxvdCh0cmVuZHNldHRlcl9kZWdyZWVzMiwgY29uZm9ybWlzdF9kZWdyZWVzMiwgDQogICAgICAgIG5hbWVzID0gYygiVHJlbmRzZXR0ZXJzIiwgIkNvbmZvcm1pc3RzIiksDQogICAgICAgIGNvbCA9IGMoIiNGRkQ3MDAiLCAiIzgwMDA4MCIpLA0KICAgICAgICBtYWluID0gImRlZ3JlZSBkaXN0cmlidXRpb24iKQ0KDQpkZWdyZWVzMyA8LSBkZWdyZWUobmV0d29yazMpDQp0cmVuZHNldHRlcl9kZWdyZWVzMyA8LSBkZWdyZWVzM1tWKG5ldHdvcmszKSRyb2xlID09ICJ0cmVuZHNldHRlciJdDQpjb25mb3JtaXN0X2RlZ3JlZXMzIDwtIGRlZ3JlZXMzW1YobmV0d29yazMpJHJvbGUgPT0gImNvbmZvcm1pc3QiXQ0KdmlvcGxvdCh0cmVuZHNldHRlcl9kZWdyZWVzMywgY29uZm9ybWlzdF9kZWdyZWVzMywgDQogICAgICAgIG5hbWVzID0gYygiVHJlbmRzZXR0ZXJzIiwgIkNvbmZvcm1pc3RzIiksDQogICAgICAgIGNvbCA9IGMoIiNGRkQ3MDAiLCAiIzgwMDA4MCIpLA0KICAgICAgICBtYWluID0gImRlZ3JlZSBkaXN0cmlidXRpb24iKQ0KYGBgDQoNCi0tLQ0KDQpTcGVjaWZ5IGEgcGFyYW1ldGVyIHNwYWNlIHRvIGdlbmVyYXRlIG5ldHdvcmtzIHdpdGggaW5kZXBlbmRlbnRseSB2YXJpZWQgZGVncmVlIGRpc3RyaWJ1dGlvbiwgZGVncmVlIGFzc29ydGF0aXZpdHksIGRlZ3JlZS10cmFpdCBjb3JyZWxhdGlvbjoNCg0KYGBge3J9DQojIHN0cnVjdHVyYWwgcGFyYW1ldGVycyANCm4gPSA5Ng0Ka19taW4gPSAzDQpwX3QgPSAwLjENCg0KIyB0YXJnZXQgcGFyYW1ldGVycw0KYWxwaGFzIDwtIGMoMi40LCAyLjcsIDMpDQpkaXN0cmlidXRpb25zIDwtIGMoInBvd2VyLWxhdyIsICJsb2ctbm9ybWFsIikNCnRhcmdldF9yX3ZhbHVlcyA8LSBsaXN0KHNlcSgtMC40LCAtMC4xNSwgYnkgPSAwLjA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHNlcSgtMC4zNSwgLTAuMDUsIGJ5ID0gMC4wNSksDQogICAgICAgICAgICAgICAgICAgICAgICBzZXEoLTAuMywgMC4xLCBieSA9IDAuMDUpDQogICAgICAgICAgICAgICAgICAgICAgICApDQpuYW1lcyh0YXJnZXRfcl92YWx1ZXMpIDwtIGFscGhhcw0KdGFyZ2V0X3Job192YWx1ZXMgPC0gc2VxKDAsIDAuNiwgYnkgPSAwLjEpDQoNCiMgbGlzdCBmb3IgcmVzdWx0cw0KcmVzdWx0cyA8LSBsaXN0KCkNCg0KZm9yIChkaXN0IGluIGRpc3RyaWJ1dGlvbnMpIHsNCiAgZm9yIChhbHBoYSBpbiBhbHBoYXMpIHsNCiAgICAjIGdlbmVyYXRlIGRlZ3JlZSBzZXF1ZW5jZSBmcm9tIHNwZWNpZmllZCBkaXN0cmlidXRpb24gYW5kIGFscGhhDQogICAgZGVnc2VxIDwtIGZkZWdzZXEobiA9IG4sIGRpc3QgPSBkaXN0LCBhbHBoYSA9IGFscGhhLCBrX21pbiA9IGtfbWluLCBzZWVkID0gMTIzKQ0KICAgIA0KICAgICMgY3JlYXRlIGFuIHVuZGlyZWN0ZWQsIGNvbm5lY3RlZCwgc2ltcGxlIGdyYXBoIHVzaW5nIFZpZ2VyLUxhdGFweSBhbGdvcml0aG0NCiAgICBuZXR3b3JrIDwtIHNhbXBsZV9kZWdzZXEoZGVnc2VxLCBtZXRob2QgPSAidmwiKQ0KICAgIA0KICAgICMgYXNzaWduIHJvbGVzIHJhbmRvbWx5LCBiYXNlZCBvbiBwcm9wb3J0aW9uIHRyZW5kc2V0dGVyIHBfdA0KICAgIFYobmV0d29yaykkcm9sZSA8LSBzYW1wbGUoYyhyZXAoInRyZW5kc2V0dGVyIiwgZmxvb3IobipwX3QpKSwgcmVwKCJjb25mb3JtaXN0IiwgbiAtIGZsb29yKG4qcF90KSkpKQ0KICAgIA0KICAgIGZvciAodGFyZ2V0X3IgaW4gdGFyZ2V0X3JfdmFsdWVzW1thcy5jaGFyYWN0ZXIoYWxwaGEpXV0pIHsgI2xvb3Agb3ZlciB0YXJnZXRfciB2YWx1ZXMNCiAgICAgIHJld2lyZWRfbmV0d29yayA8LSBmcmV3aXJlX3IobmV0d29yaywgdGFyZ2V0X3IsIHZlcmJvc2U9RkFMU0UpDQogICAgICBhY3R1YWxfciA8LSBhc3NvcnRhdGl2aXR5X2RlZ3JlZShyZXdpcmVkX25ldHdvcmspDQogICAgICANCiAgICAgIGZvciAodGFyZ2V0X3JobyBpbiB0YXJnZXRfcmhvX3ZhbHVlcykgeyAjbG9vcCBvdmVyIHRhcmdldF9yaG8gdmFsdWVzDQogICAgICAgIGZpbmFsX25ldHdvcmsgPC0gZnN3YXBfcmhvKHJld2lyZWRfbmV0d29yaywgdGFyZ2V0X3JobywgdmVyYm9zZT1GQUxTRSkNCiAgICAgICAgZmluYWxfcmhvIDwtIGZkZWd0cmFpdGNvcihmaW5hbF9uZXR3b3JrKSRjb3INCiAgICAgICAgDQogICAgICAgICMgYW5kIGFzc3VtZSB0aGF0IGFnZW50cyBhY3QgYWNjb3JkaW5nIHRvIHRoZWlyIHJvbGUNCiAgICAgICAgIyBhZ2VudHMgZG9uJ3Qgb2JzZXJ2ZSBlYWNoIG90aGVycycgcm9sZXMsIGJ1dCBkbyBvYnNlcnZlIGFjdGlvbnMgKGJhc2VkIG9uIHdoaWNoIHRoZXkgY2FuIGluZmVyIHJvbGVzL3ByZWZlcmVuY2VzLi4uKQ0KICAgICAgICANCiAgICAgICAgVihmaW5hbF9uZXR3b3JrKSRhY3Rpb24gPC0gaWZlbHNlKFYoZmluYWxfbmV0d29yaykkcm9sZSA9PSAidHJlbmRzZXR0ZXIiLCAxLCAwKQ0KICAgICAgICANCiAgICAgICAgIyBzdG9yZSByZXN1bHRzDQogICAgICAgIHJlc3VsdHMgPC0gYXBwZW5kKHJlc3VsdHMsIGxpc3QobGlzdCgNCiAgICAgICAgICBkaXN0cmlidXRpb24gPSBkaXN0LA0KICAgICAgICAgIGFscGhhID0gYWxwaGEsDQogICAgICAgICAgdGFyZ2V0X3IgPSB0YXJnZXRfciwNCiAgICAgICAgICBhY3R1YWxfciA9IGFjdHVhbF9yLA0KICAgICAgICAgIHRhcmdldF9yaG8gPSB0YXJnZXRfcmhvLA0KICAgICAgICAgIGFjdHVhbF9yaG8gPSBmaW5hbF9yaG8sDQogICAgICAgICAgbmV0d29yayA9IGZpbmFsX25ldHdvcmsNCiAgICAgICAgICApKSkNCiAgICAgIH0NCiAgICB9DQogIH0NCn0NCmBgYA0KDQo8YnI+DQoNClNhbXBsZSBzb21lIHNpbXVsYXRpb25zIGZyb20gdGhlICduZXR3b3JrIHVuaXZlcnNlJzoNCg0KYGBge3J9DQojIGV4Y2x1ZGluZyB0aGUgZ3JhcGggb2JqZWN0Og0KbGFwcGx5KHNhbXBsZShyZXN1bHRzLCA1KSwgZnVuY3Rpb24oeCkgew0KICB6IDwtIHggIyBjb3B5DQogIHokbmV0d29yayA8LSBOVUxMDQogIHoNCn0pDQpgYGANCg0KLS0tDQoNCiMgIm1ham9yaXR5IGlsbHVzaW9uIg0KDQpBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgbWFnbml0dWRlIG9mIHRoZSAibWFqb3JpdHkgaWxsdXNpb24iLCBiYXNlZCBvbiB0aGUgbmV0d29yayBzdHJ1Y3R1cmUsIGFuZCBhIGdpdmVuIG1ham9yaXR5IHRocmVzaG9sZCAoaGVyZSwgIndlYWsgaW5mbHVlbmNlIiwgc28gKm1vcmUqIHRoYW4gaGFsZiBvZiBuZWlnaGJvcnMgbmVlZCB0byBhZG9wdCB0aGUgdHJlbmQgaW4gb3JkZXIgZm9yIGFuIGVnbyB0byBiZSBwZXJzdWFkZWQgdG8gZG8gdGhlIHNhbWUpOg0KDQpgYGB7cn0NCmNhbGN1bGF0ZV9tYWpvcml0eV9pbGx1c2lvbiA8LSBmdW5jdGlvbihuZXR3b3JrLCB0aHJlc2hvbGQgPSAwLjUpIHsNCiAgcm9sZXMgPC0gVihuZXR3b3JrKSRyb2xlDQogIGFjdGlvbnMgPC0gVihuZXR3b3JrKSRhY3Rpb24NCg0KICAjaW5pdGlhbGl6ZSBjb3VudGVyIGZvciBtYWpvcml0eSBpbGx1c2lvbg0KICBtaV9jb3VudCA8LSAwDQogIA0KICAjbG9vcCBvdmVyIGNvbmZvcm1pc3RzDQogIGZvciAodiBpbiBWKG5ldHdvcmspKSB7DQogICAgaWYgKHJvbGVzW3ZdID09ICJjb25mb3JtaXN0Iikgew0KICAgICAgbmVpZ2hib3JzIDwtIG5laWdoYm9ycyhuZXR3b3JrLCB2KQ0KICAgICAgdHJlbmRfbmVpZ2hib3JzIDwtIHN1bShhY3Rpb25zW25laWdoYm9yc10gPT0gMSkNCiAgICAgIHByb3BfdHJlbmQgPC0gdHJlbmRfbmVpZ2hib3JzIC8gbGVuZ3RoKG5laWdoYm9ycykNCiAgICAgIA0KICAgICAgaWYgKHByb3BfdHJlbmQgPiB0aHJlc2hvbGQpIHsgI3VuZGVyICJzdHJvbmcgaW5mbHVlbmNlIiBoYWxmIG9mIHRoZSBuZWlnaGJvcmhvb2Qgc3VmZmljZXM7IHNvIHNldCB0aHJlc2hvbGQgYXQgLjQ5DQogICAgICAgIG1pX2NvdW50IDwtIG1pX2NvdW50ICsgMQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICAjIHJldHVybiBmcmFjdGlvbiBvZiBjb25mb3JtaXN0cyB3aG8gaGF2ZSBtYWpvcml0eSBpbGx1c2lvbg0KICByZXR1cm4obWlfY291bnQgLyBzdW0ocm9sZXMgPT0gImNvbmZvcm1pc3QiKSkNCn0NCmBgYCANCg0KPGJyPg0KDQpBcHBseSB0aGUgZnVuY3Rpb24gb3ZlciBvdXIgJ25ldHdvcmsgdW5pdmVyc2UnLCB3aGVyZSBlYWNoIG5ldHdvcmsgY29ycmVzcG9uZHMgdG8gYSBzcGVjaWZpYyBwYXJhbWV0ZXIgc3BhY2Ugcm93LCBnZW5lcmF0ZWQgdXNpbmcgYSB1bmlxdWUgc2VlZDoNCg0KYGBge3IsIGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTh9DQpwbG90ZGF0YSA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkocmVzdWx0cywgZnVuY3Rpb24ocmVzKSB7DQogIGRpc3QgPC0gZmFjdG9yKHJlcyRkaXN0LCBsZXZlbHMgPSBjKCJwb3dlci1sYXciLCAibG9nLW5vcm1hbCIpKQ0KICBhbHBoYSA8LSByZXMkYWxwaGENCiAgciA8LSByZXMkYWN0dWFsX3INCiAgcmhvIDwtIHJlcyRhY3R1YWxfcmhvDQogIG5ldHdvcmsgPC0gcmVzJG5ldHdvcmsNCiAgDQogICMgY2FsY3VsYXRlIHRoZSBtYWpvcml0eSBpbGx1c2lvbiAoaS5lLiwgdGhlIHByb3BvcnRpb24gb2YgY29uZm9ybWlzdHMgd2hvc2UgbmVpZ2hib3JzIG1lZXQgb3IgZXhjZWVkIHRocmVzaG9sZCDPhikNCiAgbWkgPC0gY2FsY3VsYXRlX21ham9yaXR5X2lsbHVzaW9uKG5ldHdvcmspDQogIA0KICAjIGNyZWF0ZSBhIGRhdGEtZnJhbWUNCiAgZGF0YS5mcmFtZShkaXN0ID0gZGlzdCwNCiAgICAgICAgICAgICBhbHBoYSA9IGFscGhhLCANCiAgICAgICAgICAgICByID0gciwgDQogICAgICAgICAgICAgcmhvID0gcmhvLCANCiAgICAgICAgICAgICBtaSA9IG1pKQ0KfSkpDQoNCiNtYWtlIHNlcGFyYXRlIGRhdGEtZnJhbWVzIGZvciBlYWNoIGxldmVsIG9mIGFscGhhDQphbHBoYTEgPC0gcGxvdGRhdGFbcGxvdGRhdGEkYWxwaGEgPT0gdW5pcXVlKHBsb3RkYXRhJGFscGhhKVsxXSwgXQ0KYWxwaGEyIDwtIHBsb3RkYXRhW3Bsb3RkYXRhJGFscGhhID09IHVuaXF1ZShwbG90ZGF0YSRhbHBoYSlbMl0sIF0NCmFscGhhMyA8LSBwbG90ZGF0YVtwbG90ZGF0YSRhbHBoYSA9PSB1bmlxdWUocGxvdGRhdGEkYWxwaGEpWzNdLCBdDQoNCiNjcmVhdGUgYmlucyBmb3IgciAoZGVnLWFzc29yYXRpdml0eSkNCmZjcmVhdGVfYmlucyA8LSBmdW5jdGlvbihkYXRhLCB2YXJpYWJsZSA9ICJyIiwgb3V0ID0gNikgew0KICBydmFscyA8LSBkYXRhW1t2YXJpYWJsZV1dDQogIHF1YW50IDwtIHF1YW50aWxlKHJ2YWxzLCBwcm9icyA9IHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gb3V0KSkgDQogICMgZ2VuZXJhdGUgbGFiZWxzIGR5bmFtaWNhbGx5DQogIGxhYmVscyA8LSBzYXBwbHkoMToobGVuZ3RoKHF1YW50KSAtIDEpLCBmdW5jdGlvbihpKSB7DQogICAgcGFzdGUwKHJvdW5kKHF1YW50W2ldLCAyKSwgIiA8IHIg4omkICIsIHJvdW5kKHF1YW50W2kgKyAxXSwgMikpDQogIH0pDQogICMgYWRkIGNhdGVnb3JpZXMgdG8gdGhlIGRhdGFzZXQNCiAgZGF0YSRyX2NhdHMgPC0gY3V0KHJ2YWxzLCBicmVha3MgPSBxdWFudCwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLCBsYWJlbHMgPSBsYWJlbHMpDQogIHJldHVybihkYXRhKQ0KfQ0KDQojIGFwcGx5IGJpbm5pbmcgIHRvIGVhY2ggc3Vic2V0DQphbHBoYTEgPC0gZmNyZWF0ZV9iaW5zKGFscGhhMSkNCmFscGhhMiA8LSBmY3JlYXRlX2JpbnMoYWxwaGEyKQ0KYWxwaGEzIDwtIGZjcmVhdGVfYmlucyhhbHBoYTMpDQoNCnBsb3QxIDwtIGdncGxvdChhbHBoYTEsIGFlcyh4ID0gcmhvLCB5ID0gbWksIGNvbG9yID0gYXMuZmFjdG9yKHJfY2F0cykpKSArDQogIGZhY2V0X3dyYXAofmRpc3QsIG5yb3cgPSAyKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAyKSArDQogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIG1ldGhvZCA9ICJsb2VzcyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuMDUsIDAuNykpICsNCiAgbGFicygNCiAgICB4ID0gZXhwcmVzc2lvbigiZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uICgiIH4gcmhvW2t4XSB+ICIpIiksIA0KICAgIHkgPSAicHJvcC4gY29uZm9ybWlzdHMgdy8gcHJvcC4gdHJlbmRzZXR0ZXIgbmJoLiA+IM+GIiwgICAgICANCiAgICBjb2xvciA9IGV4cHJlc3Npb24oImRlZ3JlZSBhc3NvcnRhdGl2aXR5ICgiIH4gcltra10gfiAiKSIpDQogICkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMywgMC4zNSkpICsgZ2d0aXRsZShwYXN0ZTAoIs6xPSIsIGFscGhhMSRhbHBoYSkpDQoNCnBsb3QyIDwtIGdncGxvdChhbHBoYTIsIGFlcyh4ID0gcmhvLCB5ID0gbWksIGNvbG9yID0gYXMuZmFjdG9yKHJfY2F0cykpKSArDQogIGZhY2V0X3dyYXAofmRpc3QsIG5yb3cgPSAyKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAyKSArDQogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIG1ldGhvZCA9ICJsb2VzcyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuMDUsIDAuNykpICsNCiAgbGFicygNCiAgICB4ID0gZXhwcmVzc2lvbigiZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uICgiIH4gcmhvW2t4XSB+ICIpIiksIA0KICAgIHkgPSAicHJvcC4gY29uZm9ybWlzdHMgdy8gcHJvcC4gdHJlbmRzZXR0ZXIgbmJoLiA+IM+GIiwgICAgICANCiAgICBjb2xvciA9IGV4cHJlc3Npb24oImRlZ3JlZSBhc3NvcnRhdGl2aXR5ICgiIH4gcltra10gfiAiKSIpDQogICkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMywgMC4zNSkpICsgZ2d0aXRsZShwYXN0ZTAoIs6xPSIsIGFscGhhMiRhbHBoYSkpDQoNCnBsb3QzIDwtIGdncGxvdChhbHBoYTMsIGFlcyh4ID0gcmhvLCB5ID0gbWksIGNvbG9yID0gYXMuZmFjdG9yKHJfY2F0cykpKSArDQogIGZhY2V0X3dyYXAofmRpc3QsIG5yb3cgPSAyKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAyKSArDQogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIG1ldGhvZCA9ICJsb2VzcyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuMDUsIDAuNykpICsNCiAgbGFicygNCiAgICB4ID0gZXhwcmVzc2lvbigiZGVncmVlLXRyYWl0IGNvcnJlbGF0aW9uICgiIH4gcmhvW2t4XSB+ICIpIiksIA0KICAgIHkgPSAicHJvcC4gY29uZm9ybWlzdHMgdy8gcHJvcC4gdHJlbmRzZXR0ZXIgbmJoLiA+IM+GIiwgICAgICANCiAgICBjb2xvciA9IGV4cHJlc3Npb24oImRlZ3JlZSBhc3NvcnRhdGl2aXR5ICgiIH4gcltra10gfiAiKSIpDQogICkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMywgMC4zNSkpICsgZ2d0aXRsZShwYXN0ZTAoIs6xPSIsIGFscGhhMyRhbHBoYSkpDQoNCiNjb21iaW5lDQpnZ2FycmFuZ2UocGxvdDEscGxvdDIscGxvdDMsbmNvbD0zKSArDQogIGdndGl0bGUoJyAiTWFqb3JpdHkgaWxsdXNpb24iIGluIG5ldHdvcmtzIHdpdGggYSBwb3dlci1sYXcgYW5kIGxvZy1ub3JtYWwgZGVncmVlIGRpc3RyaWJ1dGlvbicgKSArIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNikNCiAgKQ0KYGBgIA0KDQo8YnI+DQoNCk5vdyBmb3IgZWFjaCBwYXJhbWV0ZXIgc3BhY2Ugcm93IGdlbmVyYXRlIG5ldHdvcmtzIG92ZXIgTiBkaWZmZXJlbnQgc2VlZHM6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBzZXQgdXAgcGFyYWxsZWwgYmFja2VuZCB0byBpbmNyZWFzZSBlZmZpY2llbmN5DQpuY29yZXMgPC0gZGV0ZWN0Q29yZXMoKSAtIDEgDQpjbCA8LSBtYWtlQ2x1c3RlcihuY29yZXMpDQpyZWdpc3RlckRvUGFyYWxsZWwoY2wpDQoNCiMgbnVtYmVyIG9mIHNlZWRzDQpuSXRlciA8LSAxMDANCmRpc3RyaWJ1dGlvbnMgPC0gYygicG93ZXItbGF3IiwgImxvZy1ub3JtYWwiKQ0KDQojIGxpc3QgdG8gc3RvcmUgcmVzdWx0cyBpbg0KcmVwX3Jlc3VsdHMgPC0gbGlzdCgpDQoNCiMgcGFyYWxsZWwgcHJvY2Vzc2luZyB1c2luZyBmb3JlYWNoDQpyZXBfcmVzdWx0cyA8LSBmb3JlYWNoKGFscGhhID0gYWxwaGFzLCAuY29tYmluZSA9ICdjJywgLnBhY2thZ2VzID0gYygiaWdyYXBoIikpICU6JQ0KICBmb3JlYWNoKGl0ZXIgPSAxOm5JdGVyLCAuY29tYmluZSA9ICdjJywgLnBhY2thZ2VzID0gYygiaWdyYXBoIikpICVkb3BhciUgew0KICAgIA0KICAgICMgc2VlZCBtdXN0IHZhcnkgd2l0aCBlYWNoIGl0ZXJhdGlvbg0KICAgIHNlZWQgPC0gMTIzICsgaXRlcg0KICAgIHNldC5zZWVkKHNlZWQpDQogICAgDQogICAgcHJpbnQocGFzdGUwKCJSdW5uaW5nIGl0ZXJhdGlvbiAiLCBpdGVyLCAiLyIsIG5JdGVyLCAiIGZvciBhbHBoYSA9ICIsIGFscGhhKSkNCiAgICANCiAgICByZXN1bHRzIDwtIGxpc3QoKSAgI3RlbXBvcmFyeSBzdG9yYWdlIGZvciB0aGUgcmVzdWx0cw0KICAgIA0KICAgICMgbG9vcCBvdmVyIGRpc3RyaWJ1dGlvbiB0eXBlczoNCiAgICBmb3IgKGRpc3QgaW4gZGlzdHJpYnV0aW9ucykgew0KICAgICAgIyBnZW5lcmF0ZSBkZWdyZWUgc2VxdWVuY2Ugd2l0aCBzcGVjaWZpZWQgZGlzdHJpYnV0aW9uIHR5cGUNCiAgICAgIGRlZ3NlcSA8LSBmZGVnc2VxKG4gPSBuLCBhbHBoYSA9IGFscGhhLCBrX21pbiA9IGtfbWluLCBzZWVkID0gMTIzICsgaXRlciwgZGlzdCA9IGRpc3QpDQogICAgICANCiAgICAgICMgY29uc3RydWN0IHRoZSBuZXR3b3JrDQogICAgICBuZXR3b3JrIDwtIHNhbXBsZV9kZWdzZXEoZGVnc2VxLCBtZXRob2QgPSAidmwiKQ0KICAgICAgDQogICAgICAjIGFzc2lnbiByb2xlcyByYW5kb21seQ0KICAgICAgVihuZXR3b3JrKSRyb2xlIDwtIHNhbXBsZShjKHJlcCgidHJlbmRzZXR0ZXIiLCBmbG9vcihuICogcF90KSksIHJlcCgiY29uZm9ybWlzdCIsIG4gLSBmbG9vcihuICogcF90KSkpKQ0KICAgICAgDQogICAgICAjIGxvb3Agb3ZlciB0YXJnZXRfciB2YWx1ZXMNCiAgICAgIGZvciAodGFyZ2V0X3IgaW4gdGFyZ2V0X3JfdmFsdWVzW1thcy5jaGFyYWN0ZXIoYWxwaGEpXV0pIHsNCiAgICAgICAgcmV3aXJlZF9uZXR3b3JrIDwtIGZyZXdpcmVfcihuZXR3b3JrLCB0YXJnZXRfciwgdmVyYm9zZSA9IEZBTFNFLCBtYXhfaXRlciA9IDFlNCkNCiAgICAgICAgYWN0dWFsX3IgPC0gYXNzb3J0YXRpdml0eV9kZWdyZWUocmV3aXJlZF9uZXR3b3JrKQ0KICAgICAgICANCiAgICAgICAgIyBsb29wIG92ZXIgdGFyZ2V0X3JobyB2YWx1ZXMNCiAgICAgICAgZm9yICh0YXJnZXRfcmhvIGluIHRhcmdldF9yaG9fdmFsdWVzKSB7DQogICAgICAgICAgZmluYWxfbmV0d29yayA8LSBmc3dhcF9yaG8ocmV3aXJlZF9uZXR3b3JrLCB0YXJnZXRfcmhvLCB2ZXJib3NlID0gRkFMU0UpDQogICAgICAgICAgZmluYWxfcmhvIDwtIGZkZWd0cmFpdGNvcihmaW5hbF9uZXR3b3JrKSRjb3INCiAgICAgICAgICANCiAgICAgICAgICAjdXBkYXRlIGNob2ljZXMgKGNob2ljZT09cm9sZSk6IA0KICAgICAgICAgIFYoZmluYWxfbmV0d29yaykkYWN0aW9uIDwtIGlmZWxzZShWKGZpbmFsX25ldHdvcmspJHJvbGUgPT0gInRyZW5kc2V0dGVyIiwgMSwgMCkNCiAgICAgICAgICANCiAgICAgICAgICAjYW5kIGNhbGN1bGF0ZSBnbG9iYWwgbWFqb3JpdHkgaWxsdXNpb24NCiAgICAgICAgICBtaSA8LSBjYWxjdWxhdGVfbWFqb3JpdHlfaWxsdXNpb24oZmluYWxfbmV0d29yaykNCiAgICAgICAgICANCiAgICAgICAgICAjIGFwcGVuZCByZXN1bHRzDQogICAgICAgICAgcmVzdWx0cyA8LSBhcHBlbmQocmVzdWx0cywgbGlzdChsaXN0KA0KICAgICAgICAgICAgZGlzdCA9IGRpc3QsDQogICAgICAgICAgICBhbHBoYSA9IGFscGhhLA0KICAgICAgICAgICAgdGFyZ2V0X3IgPSB0YXJnZXRfciwNCiAgICAgICAgICAgIGFjdHVhbF9yID0gYWN0dWFsX3IsDQogICAgICAgICAgICB0YXJnZXRfcmhvID0gdGFyZ2V0X3JobywNCiAgICAgICAgICAgIGFjdHVhbF9yaG8gPSBmaW5hbF9yaG8sDQogICAgICAgICAgICBtaSA9IG1pLA0KICAgICAgICAgICAgc2VlZCA9IHNlZWQsICNzdG9yZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCiAgICAgICAgICAgIG5ldHdvcmsgPSBmaW5hbF9uZXR3b3JrICMgc3RvcmUgbmV0d29ya3MsIHNvIHdlIGNhbiBhY2Nlc3MgdGhlc2UgZm9yIHRoZSBzaW11bGF0aW9ucy4NCiAgICAgICAgKSkpDQogICAgICB9DQogICAgICB9DQogICAgfQ0KICAgIHJlc3VsdHMNCiAgfQ0KDQpzdG9wQ2x1c3RlcihjbCkNCg0KI3NhdmUgb3V0cHV0Li4NCmZzYXZlKHJlcF9yZXN1bHRzLCAibmV0d29ya3MuUmRhIikNCmBgYA0KDQo8YnI+DQoNClZpc3VhbGl6ZSB0aGUgZXh0ZW50IG9mIG1ham9yaXR5IGlsbHVzaW9uIGRlcGVuZGluZyBvbiB0aGUgbmV0d29yayBwYXJhbWV0ZXJzIGFjcm9zcyB0d28gZGVncmVlIGRpc3RyaWJ1dGlvbiB0eXBlcywgYmFzZWQgb24gbmV0d29yayBzaW11bGF0aW9ucyB3aXRoIGRpZmZlcmVudCBzZWVkcy4gVmlzdWFsaXphdGlvbnMgaW5jbHVkZSBib3RoIG9ic2VydmVkIE1JIGFuZCBwcmVkaWN0ZWQgdmFsdWVzIGZyb20gT0xTIHJlZ3Jlc3Npb24uDQoNCmBgYHtyfQ0KbmV0cyA8LSBmbG9hZCgiLi9kYXRhL3Byb2Nlc3NlZC8yMDI1MDEwN25ldHdvcmtzLlJkYSIpDQoNCiMgdG8gYSBkYXRhZnJhbWU6DQpkZiA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkobmV0cywgZnVuY3Rpb24ocmVzKSB7DQogIGRhdGEuZnJhbWUoDQogICAgc2VlZCA9IHJlcyRzZWVkLA0KICAgIGRpc3QgPSByZXMkZGlzdCwNCiAgICBhbHBoYSA9IHJlcyRhbHBoYSwNCiAgICB0YXJnZXRfciA9IHJlcyR0YXJnZXRfciwNCiAgICBhY3R1YWxfciA9IHJlcyRhY3R1YWxfciwNCiAgICB0YXJnZXRfcmhvID0gcmVzJHRhcmdldF9yaG8sDQogICAgYWN0dWFsX3JobyA9IHJlcyRhY3R1YWxfcmhvLA0KICAgIG1pID0gcmVzJG1pDQogICkNCn0pKQ0KDQojIGNyZWF0ZSBhIDNEIHNjYXR0ZXJwbG90IG9mIG9ic2VydmVkIHZhbHVlcyBvZiBNSSwgZGlzYWdncmVnYXRlZCBieQ0KIyBkZWdyZWUgZGlzdHJpYnV0aW9uIHR5cGUNCmZpZzEgPC0gcGxvdF9seSgpDQoNCmZvciAoZGlzdCBpbiB1bmlxdWUoZGYkZGlzdCkpIHsNCiAgZmlnMSA8LSBmaWcxICU+JQ0KICAgIGFkZF90cmFjZSgNCiAgICAgIGRhdGEgPSBkZltkZiRkaXN0ID09IGRpc3QsIF0sDQogICAgICB4ID0gfmFscGhhLA0KICAgICAgeSA9IH5hY3R1YWxfcmhvLA0KICAgICAgeiA9IH5hY3R1YWxfciwNCiAgICAgIGNvbG9yID0gfm1pLA0KICAgICAgdHlwZSA9ICdzY2F0dGVyM2QnLA0KICAgICAgbW9kZSA9ICdtYXJrZXJzJywNCiAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDMpLA0KICAgICAgbmFtZSA9IGFzLmNoYXJhY3RlcihkaXN0KSwNCiAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZSgNCiAgICAgICAgIs6xOiAle3h9PGJyPiIsDQogICAgICAgICLPgV97eGt9OiAle3l9PGJyPiIsDQogICAgICAgICJyX3tra306ICV7en08YnI+IiwNCiAgICAgICAgIk1JOiAle21hcmtlci5jb2xvcn08ZXh0cmE+PC9leHRyYT4iDQogICAgICApDQogICAgKSANCn0NCg0KIyBhZGQgbGF5b3V0IGRldGFpbHM6DQpmaWcxIDwtIGZpZzEgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICczRCBzY2F0dGVycGxvdCBvZiB0aGUgbWFnbml0dWRlIG9mIHRoZSAibWFqb3JpdHkgaWxsdXNpb24iIGJ5IGRlZ3JlZSBkaXN0cmlidXRpb24gdHlwZScsDQogICAgc2NlbmUgPSBsaXN0KA0KICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIs6xIiksDQogICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiz4Ffe3hrfSIpLA0KICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gInJfe2trfSIpDQogICAgKQ0KICApICU+JQ0KICBjb2xvcmJhcih0aXRsZT0iTWFqb3JpdHkgaWxsdXNpb24iKQ0KDQoNCiNmaXQgbW9kZWxzID0+IHByZWRpY3RlZCB2YWx1ZXMNCm0xIDwtIGxtKG1pIH4gYWN0dWFsX3JobyArIGFjdHVhbF9yICsgYXMuZmFjdG9yKGFscGhhKSwgZGF0YSA9IGRmW2RmJGRpc3QgPT0gInBvd2VyLWxhdyIsXSkNCm0yIDwtIGxtKG1pIH4gYWN0dWFsX3JobyArIGFjdHVhbF9yICsgYXMuZmFjdG9yKGFscGhhKSArIGFjdHVhbF9yaG86YWN0dWFsX3IsIGRhdGEgPSBkZltkZiRkaXN0ID09ICJwb3dlci1sYXciLF0pDQptMyA8LSBsbShtaSB+IGFjdHVhbF9yaG8gKyBhY3R1YWxfciArIGFzLmZhY3RvcihhbHBoYSkgKyBhY3R1YWxfcmhvOmFjdHVhbF9yICsgYWN0dWFsX3Jobzphcy5mYWN0b3IoYWxwaGEpICsgYWN0dWFsX3I6YXMuZmFjdG9yKGFscGhhKSwgZGF0YSA9IGRmW2RmJGRpc3QgPT0gInBvd2VyLWxhdyIsXSkNCg0KI2Fub3ZhKG0xLG0yLG0zKQ0KI3N1bW1hcnkobTMpDQoNCiMgY3JlYXRlIGEgc2VxdWVuY2Ugb2YgdmFsdWVzIGZvciByaG8sIHIsIGFuZCBhbHBoYQ0KcmhvX3ZhbHMgPC0gc2VxKG1pbihkZltkZiRkaXN0ID09ICJwb3dlci1sYXciLF0kYWN0dWFsX3JobyksIG1heChkZltkZiRkaXN0ID09ICJwb3dlci1sYXciLF0kYWN0dWFsX3JobyksIGxlbmd0aC5vdXQgPSA1MCkNCnJfdmFscyA8LSBzZXEobWluKGRmW2RmJGRpc3QgPT0gInBvd2VyLWxhdyIsXSRhY3R1YWxfciksIG1heChkZltkZiRkaXN0ID09ICJwb3dlci1sYXciLF0kYWN0dWFsX3IpLCBsZW5ndGgub3V0ID0gNTApDQojYWxwaGFfdmFscyA8LSBzZXEoMi40LCAzLCBieT0wLjEpICANCmFscGhhX3ZhbHMgPC0gYygyLjQsMi43LDMpDQoNCiMgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgdGhlIHN1cmZhY2UgZGF0YSBmb3IgYSBnaXZlbiBhbHBoYSB2YWx1ZQ0KZnN1cmZhY2VkYXQgPC0gZnVuY3Rpb24oYWxwaGFfdmFsKSB7DQogICMgY3JlYXRlIGEgZ3JpZCBvZiByIGFuZCByaG8gdmFsdWVzDQogIGdyaWQgPC0gZXhwYW5kLmdyaWQoYWN0dWFsX3IgPSByX3ZhbHMsIGFjdHVhbF9yaG8gPSByaG9fdmFscykNCiAgDQogICMgZml4ZWQgYWxwaGEgdmFsdWUNCiAgZ3JpZCRhbHBoYSA8LSBhbHBoYV92YWwNCiAgDQogICMgcHJlZGljdCB0aGUgdmFsdWVzIG9mIG1pIHVzaW5nIHRoZSBtb2RlbA0KICBncmlkJG1pX3ByZWQgPC0gcHJlZGljdChtMywgbmV3ZGF0YSA9IGdyaWQpDQogIA0KICAjIHJlc2hhcGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgaW50byBhIG1hdHJpeA0KICBtaV9tYXRyaXggPC0gbWF0cml4KGdyaWQkbWlfcHJlZCwgbnJvdyA9IGxlbmd0aChyX3ZhbHMpLCBuY29sID0gbGVuZ3RoKHJob192YWxzKSwgYnlyb3cgPSBUUlVFKQ0KICANCiAgcmV0dXJuKG1pX21hdHJpeCkNCn0NCg0KIyBwcmVjb21wdXRlIHRoZSBzdXJmYWNlIGRhdGEgZm9yIGFsbCBhbHBoYSB2YWx1ZXMNCnN1cmZhY2VfZGF0YV9saXN0IDwtIGxhcHBseShhbHBoYV92YWxzLCBmc3VyZmFjZWRhdCkNCg0KIyBmaW5kIHRoZSBnbG9iYWwgcmFuZ2Ugb2YgTU0gKHogdmFsdWVzKQ0Kel9taW4gPC0gbWluKHNhcHBseShzdXJmYWNlX2RhdGFfbGlzdCwgbWluKSkNCnpfbWF4IDwtIG1heChzYXBwbHkoc3VyZmFjZV9kYXRhX2xpc3QsIG1heCkpDQoNCiMgY3JlYXRlIHRoZSBpbml0aWFsIHN1cmZhY2UgcGxvdCB3aXRoIHRoZSBmaXJzdCBhbHBoYSB2YWx1ZQ0KZmlnMiA8LSBwbG90X2x5KA0KICB4ID0gcl92YWxzLCAgIyB4LWF4aXM6IHINCiAgeSA9IHJob192YWxzLCAgIyB5LWF4aXM6IHJobw0KICB6ID0gc3VyZmFjZV9kYXRhX2xpc3RbWzFdXSwgICMgc3VyZmFjZSBkYXRhIGZvciB0aGUgZmlyc3QgYWxwaGENCiAgdHlwZSA9ICJzdXJmYWNlIiwNCiAgY29sb3JiYXIgPSBsaXN0KA0KICAgIHRpdGxlID0gIk1JIiwNCiAgICBjbWluID0gel9taW4sIA0KICAgIGNtYXggPSB6X21heA0KICApLA0KICBjbWluID0gel9taW4sICANCiAgY21heCA9IHpfbWF4LA0KICBob3ZlcnRlbXBsYXRlID0gcGFzdGUoDQogICAgInJfe2trfTogJXt4Oi4yZn08YnI+IiwgIA0KICAgICLPgV97eGt9OiAle3k6LjJmfTxicj4iLCAgDQogICAgIk1JOiAle3o6LjJmfTxleHRyYT48L2V4dHJhPiINCiAgKQ0KKQ0KDQojIGFkZCBzbGlkZXIgZm9yIGR5bmFtaWMgYWxwaGEgc2VsZWN0aW9uDQpmaWcyIDwtIGZpZzIgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZSA9IHBhc3RlKCdQcmVkaWN0ZWQgIm1ham9yaXR5IGlsbHVzaW9uIiBpbiBzY2FsZS1mcmVlIG5ldHdvcmsgZnJvbSBPTFMgbW9kZWwgZm9yIM6xID0nLCBhbHBoYV92YWxzWzFdKSwNCiAgICBzY2VuZSA9IGxpc3QoDQogICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAicl97a2t9IiksDQogICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiz4Ffe2t4fSIpLA0KICAgICAgemF4aXMgPSBsaXN0KA0KICAgICAgICB0aXRsZSA9ICJNSSIsDQogICAgICAgIHJhbmdlID0gYyh6X21pbiwgel9tYXgpIA0KICAgICAgKQ0KICAgICksDQogICAgc2xpZGVycyA9IGxpc3QoDQogICAgICBsaXN0KA0KICAgICAgICBhY3RpdmUgPSAwLCAgIyBzdGFydCB3aXRoIHRoZSBmaXJzdCBhbHBoYSB2YWx1ZQ0KICAgICAgICBzdGVwcyA9IGxhcHBseShzZXFfYWxvbmcoYWxwaGFfdmFscyksIGZ1bmN0aW9uKGkpIHsNCiAgICAgICAgICBsaXN0KA0KICAgICAgICAgICAgbWV0aG9kID0gInVwZGF0ZSIsDQogICAgICAgICAgICBhcmdzID0gbGlzdCgNCiAgICAgICAgICAgICAgbGlzdCgNCiAgICAgICAgICAgICAgICB6ID0gbGlzdChzdXJmYWNlX2RhdGFfbGlzdFtbaV1dKSAgI3VwZGF0ZSBzdXJmYWNlIGRhdGENCiAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgbGlzdCgNCiAgICAgICAgICAgICAgICB0aXRsZSA9IHBhc3RlKCdQcmVkaWN0ZWQgIm1ham9yaXR5IGlsbHVzaW9uIiBpbiBzY2FsZS1mcmVlIG5ldHdvcmsgZnJvbSBPTFMgbW9kZWwgZm9yIM6xID0nLCBhbHBoYV92YWxzW2ldKSAgIyB1cGRhdGUgdGl0bGUNCiAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgKSwNCiAgICAgICAgICAgIGxhYmVsID0gYXMuY2hhcmFjdGVyKGFscGhhX3ZhbHNbaV0pICAjIHNsaWRlciBsYWJlbA0KICAgICAgICAgICkNCiAgICAgICAgfSksDQogICAgICAgIGN1cnJlbnR2YWx1ZSA9IGxpc3QoDQogICAgICAgICAgcHJlZml4ID0gIs6xOiAiLCAgIyB0ZXh0IGRpc3BsYXllZCBuZXh0IHRvIHRoZSBzbGlkZXINCiAgICAgICAgICBmb250ID0gbGlzdChzaXplID0gMTYpDQogICAgICAgICkNCiAgICAgICkNCiAgICApDQogICkgICU+JQ0KICBjb2xvcmJhcih0aXRsZT0iTWFqb3JpdHkgaWxsdXNpb24iKQ0KDQpmaWcxDQpmaWcyDQpgYGAgDQoNCi0tLQ0KDQojIHNpbXVsYXRpb24gZnVuY3Rpb24NCg0KTm93IHRoYXQgd2UgaGF2ZSBvdXIgInN0YXJ0aW5nIG5ldHdvcmtzIiwgbGV0J3Mgc2ltdWxhdGUgdGhlIGV2b2x1dGlvbiBvZiBhbiB1bnBvcHVsYXIgbm9ybSB1c2luZyBvdXIgdXRpbGl0eSBmdW5jdGlvbjoNCg0KYGBge3J9DQpmYWJtIDwtIGZ1bmN0aW9uKG5ldHdvcmsgPSBuZXR3b3JrLCAjIHRoZSBnZW5lcmF0ZWQgbmV0d29yaw0KICAgICAgICAgICAgICAgICByb3VuZHMgPSAxMCwgIyBudW1iZXIgb2YgdGltZXN0ZXBzL3JvdW5kcw0KICAgICAgICAgICAgICAgICBjaG9pY2VfcnVsZSA9ICJkZXRlcm1pbmlzdGljIiwgIyBjaG9pY2UgdXBkYXRlIHJ1bGUNCiAgICAgICAgICAgICAgICAgdXRpbGl0eV9mbiA9IGZ1dGlsaXR5LCAjIHRoZSB1dGlsaXR5IGZ1bmN0aW9uDQogICAgICAgICAgICAgICAgIHBhcmFtcyA9IGxpc3Qocz0xMCwgZT0xMCwgdz0zMCwgej0zNSksICMgdXRpbGl0eSBwYXJhbWV0ZXJzDQogICAgICAgICAgICAgICAgIG1pX3RocmVzaG9sZCA9IGlmZWxzZShwYXJhbXMkeiA+IDUwLCAuNDksIC41MCksICMgdW5kZXIgdGhlICJzdHJvbmcgaW5mbHVlbmNlIiBjb25kaXRpb24gKGkuZS4sIDUwPHo8OTApIGhhbGYgb2YgbmVpZ2hib3JzIGFkb3B0aW5nIHRoZSB0cmVuZCBpcyBzdWZmaWNpZW50OyB1bmRlciB0aGUgIndlYWsgaW5mbHVlbmNlIiBjb25kaXRpb24gKGkuZS4sIDMwPHo8NTApICptb3JlKiB0aGFuIGhhbGYgb2YgbmVpZ2hib3JzIGFkb3B0aW5nIHRoZSB0cmVuZCBpcyBuZWVkZWQgdG8gYmUgaW5mbHVlbmNlZC4pDQogICAgICAgICAgICAgICAgIHBsb3QgPSBGQUxTRSApIHsgIyByZXR1cm4gcGxvdA0KICANCiAgIyBtYWtlIGFuIGFnZW50cyBkYXRhZnJhbWUNCiAgYWdlbnRzIDwtIHRpYmJsZSgNCiAgICBpZCA9IDE6bGVuZ3RoKG5ldHdvcmspLA0KICAgIHJvbGUgPSBWKG5ldHdvcmspJHJvbGUsDQogICAgcHJlZmVyZW5jZSA9IGlmZWxzZShyb2xlID09ICJ0cmVuZHNldHRlciIsIDEsIDApLCAjIDEgPSBmb2xsb3cgdHJlbmQsIDAgPSBub3QgZm9sbG93DQogICAgY2hvaWNlID0gTkENCiAgKQ0KICANCiAgIyBpbml0aWFsaXplIGRlY2lzaW9uIGhpc3RvcnkNCiAgZGVjaXNpb25faGlzdG9yeSA8LSB0aWJibGUoKQ0KICANCiAgIyBzaW11bGF0aW9uIGxvb3ANCiAgZm9yICh0IGluIDE6cm91bmRzKSB7DQogICAgaWYgKHQgPT0gMSkgew0KICAgICAgIyByb3VuZCAxOiBhZ2VudHMgbWFrZSBkZWNpc2lvbnMgYmFzZWQgb24gcHJpdmF0ZSBwcmVmZXJlbmNlcyAobm8gc29jaWFsIGluZm9ybWF0aW9uIGF2YWlsYWJsZSB5ZXQpDQogICAgICBhZ2VudHMgPC0gYWdlbnRzICU+JQ0KICAgICAgICBtdXRhdGUoY2hvaWNlID0gcHJlZmVyZW5jZSkNCiAgICB9IGVsc2Ugew0KICAgICAgICMgcm91bmQgdCA+IDE6IGFnZW50cyBtYWtlIGRlY2lzaW9ucyBiYXNlZCBvbiBzb2NpYWwgaW5mb3JtYXRpb24gZnJvbSBuZWlnaGJvcnMgKGRlY2lzaW9ucyBhdCB0LTEpDQogICAgICBhZ2VudHMgPC0gYWdlbnRzICU+JQ0KICAgICAgICByb3d3aXNlKCkgJT4lDQogICAgICAgIG11dGF0ZSgNCiAgICAgICAgICB1dGlsXzEgPSB1dGlsaXR5X2ZuKGlkLCAxLCBhZ2VudHMsIG5ldHdvcmssIGxpc3QocyA9IHBhcmFtcyRzLCBlID0gcGFyYW1zJGUsIHcgPSBwYXJhbXMkdywgeiA9IHBhcmFtcyR6KSkkdXRpbGl0eSwNCiAgICAgICAgICB1dGlsXzAgPSB1dGlsaXR5X2ZuKGlkLCAwLCBhZ2VudHMsIG5ldHdvcmssIGxpc3QocyA9IHBhcmFtcyRzLCBlID0gcGFyYW1zJGUsIHcgPSBwYXJhbXMkdywgeiA9IHBhcmFtcyR6KSkkdXRpbGl0eSwNCiAgICAgICAgICAjIG1ha2UgZGVjaXNpb24gYmFzZWQgb24gZXhwZWN0ZWQgdXRpbGl0eQ0KICAgICAgICAgIGNob2ljZSA9IGlmZWxzZSgNCiAgICAgICAgICAgIGNob2ljZV9ydWxlID09ICJkZXRlcm1pbmlzdGljIiwNCiAgICAgICAgICAgIGlmZWxzZSh1dGlsXzEgPiB1dGlsXzAsIDEsIDApLCAgIyBkZXRlcm1pbmlzdGljIHJ1bGUNCiAgICAgICAgICAgIHNhbXBsZShjKDEsIDApLCBzaXplID0gMSwgcHJvYiA9IGMoZXhwKHV0aWxfMSksIGV4cCh1dGlsXzApKSkgICMgcHJvYmFiaWxpc3RpYyBydWxlIChAUkY6IG9ubHkgZm9yIGNvbmZvcm1pc3RzPyBsZXZlbCBvZiBub2lzZT8pDQogICAgICAgICAgKQ0KICAgICAgICApDQogICAgfQ0KICAgICMgc3RvcmUgdGhlIGRlY2lzaW9ucyBmb3IgdGhpcyByb3VuZA0KICAgIGRlY2lzaW9uX2hpc3RvcnkgPC0gYmluZF9yb3dzKGRlY2lzaW9uX2hpc3RvcnksIGFnZW50cyAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUocm91bmQgPSB0KSkNCiAgfQ0KICANCiAgIyBiYXNlZCBvbiBkZWNpc2lvbl9oaXN0b3J5Og0KICANCiAgIyAxLiBjYWxjdWxhdGUgZ2xvYmFsIG1ham9yaXR5IGlsbHVzaW9uIG92ZXIgcm91bmRzDQogIGdsb2JNSSA8LSBudW1lcmljKHJvdW5kcykNCiAgZm9yICh0IGluIDE6cm91bmRzKSB7DQogICAgaWYgKHQgPT0gMSkgew0KICAgICAgIyBpbiByb3VuZCAxOiBubyBzb2NpYWwgaW5mb3JtYXRpb24sIHNvIG5vIE1JDQogICAgICBnbG9iTUlbdF0gPC0gTkENCiAgICB9IGVsc2Ugew0KICAgICAgIyByb3VuZHMgdCA+IDE6IGNhbGN1bGF0ZSBtYWduaXR1ZGUgb2YgbWFqb3JpdHkgaWxsdXNpb24NCiAgICAgICMgZmlyc3QsIG1ha2UgYSBjb3B5IG9mIHRoZSBuZXR3b3JrIG9iamVjdA0KICAgICAgZXhwb3N1cmVfbmV0d29yayA8LSBuZXR3b3JrDQogICAgICAjIHVwZGF0ZSB0aGUgYWN0aW9ucyBvZiBhY3RvcnMsIGJhc2VkIG9uIHRoZWlyIGNob2ljZXMgaW4gdGhlIHByZXZpb3VzIHJvdW5kDQogICAgICAjIGFmdGVyIGFsbCwgYWN0b3JzIGRvbid0IG9ic2VydmUgb3RoZXJzJyByb2xlcywgYnV0IG9ubHkgdGhlaXIgY2hvaWNlcywNCiAgICAgICMgYmFzZWQgb24gd2hpY2ggdGhleSBjYW4gaW5mZXIgdGhlaXIgcm9sZQ0KICAgICAgVihleHBvc3VyZV9uZXR3b3JrKSRhY3Rpb24gPC0gZGVjaXNpb25faGlzdG9yeVtkZWNpc2lvbl9oaXN0b3J5JHJvdW5kID09IHQgLSAxLF0kY2hvaWNlDQogICAgICBnbG9iTUlbdF0gPC0gY2FsY3VsYXRlX21ham9yaXR5X2lsbHVzaW9uKGV4cG9zdXJlX25ldHdvcmssIHRocmVzaG9sZCA9IG1pX3RocmVzaG9sZCkgIA0KICAgIH0NCiAgfQ0KICANCiAgIyB0byBsb25nIGRhdGEgZnJhbWU6IA0KICBNSSA8LSBkYXRhLmZyYW1lKA0KICAgIHJvdW5kID0gMTpyb3VuZHMsDQogICAgb3V0Y29tZSA9ICJtYWpvcml0eV9pbGx1c2lvbiIsDQogICAgc3RhdGlzdGljID0gZ2xvYk1JDQogICAgKQ0KICANCiAgIyAyLiBjYWxjdWxhdGUgdGhlIGV2b2x1dGlvbiBvZiB0aGUgdW5wb3B1bGFyIG5vcm0NCiAgVU4gPC0gZGVjaXNpb25faGlzdG9yeSAlPiUNCiAgICBncm91cF9ieShyb3VuZCkgJT4lDQogICAgc3VtbWFyaXNlKA0KICAgICAgZm9sbG93X3RyZW5kID0gbWVhbihjaG9pY2UgPT0gMSwgbmEucm0gPSBUUlVFKQ0KICAgICAgKSAlPiUNCiAgICAgIHBpdm90X2xvbmdlcihjb2xzID0gYygiZm9sbG93X3RyZW5kIiksDQogICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAib3V0Y29tZSIsDQogICAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInN0YXRpc3RpYyIpDQogIA0KICAjIGJpbmQNCiAgcGxvdGRhdGEgPC0gcmJpbmQoTUksIFVOKQ0KICANCiAgZmlnIDwtIGdncGxvdChwbG90ZGF0YSwgYWVzKHg9cm91bmQsIHk9c3RhdGlzdGljLCBjb2xvcj1mYWN0b3Iob3V0Y29tZSkpKSArDQogICAgICBnZW9tX2xpbmUoKSArDQogICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoc2NhbGUgPSAxMDApLCBsaW1pdHMgPSBjKDAsMSkpICsNCiAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMSwgbWF4KHBsb3RkYXRhJHJvdW5kKSwgYnkgPSAxKSkgKw0KICAgICBsYWJzKA0KICAgICAgIHRpdGxlID0gIkV2b2x1dGlvbiBvZiBhbiB1bnBvcHVsYXIgbm9ybSIsDQogICAgICAgc3VidGl0bGUgPSAiYGZvbGxvd190cmVuZGAgZGVub3RlcyB0aGUgcGVyY2VudGFnZSBvZiBhbGwgYWdlbnRzIHRoYXQgZm9sbG93IHRoZSB0cmVuZC5cbmBtYWpvcml0eV9pbGx1c2lvbmAgcmVmbGVjdHMgdGhlIHBlcmNlbnRhZ2Ugb2YgY29uZm9ybWlzdHMgd2hvc2UgbmVpZ2hib3JzIG1lZXQgb3IgZXhjZWVkIHRoZSBhZG9wdGlvbiB0aHJlc2hvbGQgz4YgKGkuZS4sIDAuNTApLFxud2l0aCAnc3Ryb25nIGluZmx1ZW5jZScgcmVmZXJyaW5nIHRvIGJvdGggbWVldGluZyBhbmQgZXhjZWVkaW5nIHRoZSB0aHJlc2hvbGQsIGFuZCAnd2VhayBpbmZsdWVuY2UnIHRvIGV4Y2VlZGluZyBpdCBvbmx5LlxuVGhlIGdyZXkgZGFzaGVkIGxpbmUgcmVmbGVjdHMgdGhlIHBlcmNlbnRhZ2Ugb2YgYWdlbnRzIHdob3NlIHJvbGUgaXMgdHJlbmRzZXR0ZXIuIiwNCiAgICAgICB4ID0gInJvdW5kIiwNCiAgICAgICB5ID0gIiUgYWdlbnRzIiwNCiAgICAgICBjb2xvciA9ICJvdXRjb21lIikgKw0KICAgICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAjbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHByb3AudGFibGUodGFibGUoYWdlbnRzJHJvbGUpKVsyXSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya2dyZXkiLCBzaXplID0gMSkNCiAgIA0KICAgaWYocGxvdD09VFJVRSkgew0KICAgICByZXR1cm4obGlzdChkZWNpc2lvbl9oaXN0b3J5PWRlY2lzaW9uX2hpc3RvcnksDQogICAgICAgICAgICAgICAgIGV2bz1wbG90ZGF0YSwNCiAgICAgICAgICAgICAgICAgcGxvdD1maWcpKQ0KICAgfSBlbHNlIHsNCiAgICAgcmV0dXJuKGxpc3QoZGVjaXNpb25faGlzdG9yeT1kZWNpc2lvbl9oaXN0b3J5LA0KICAgICAgICAgICAgICAgICBldm89cGxvdGRhdGENCiAgICAgICAgICAgICAgICAgKSkNCiAgIH0NCn0NCmBgYA0KDQoNCjxicj4NCg0KIyMgZXhhbXBsZQ0KDQpTZWxlY3QgcmFuZG9tIG5ldHdvcmsgZnJvbSB0aGUgbmV0d29ya3Mgd2l0aCB0aGUgdG9wIDEwJSBoaWdoZXN0IG1ham9yaXR5IGlsbHVzaW9uIGFuZCBzaW11bGF0ZSB0aGUgZXZvbHV0aW9uIG9mIHRoZSB0cmVuZDoNCg0KYGBge3IsIGZpZy53aWR0aD04fQ0Kc2V0LnNlZWQoMjM1NDM1KSAjIHNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkgDQoNCm1pcyA8LSBzYXBwbHkobmV0cywgZnVuY3Rpb24oeCkgeCRtaSkgIyBleHRyYWN0IE1JIHZhbHVlcw0Kc29ydGVkIDwtIG9yZGVyKG1pcykgIyBzb3J0IGluZGljZXMgYnkgTUkNCm4gPC0gbGVuZ3RoKG1pcykNCg0KdG9wMTAgPC0gc29ydGVkW2NlaWxpbmcoMC45ICogbik6bl0gIyB0b3AgMTAlIE1JDQppbmQgPC0gc2FtcGxlKHRvcDEwLCAxMCkgIyBzZWxlY3QgcmFuZG9tIGVsZW1lbnRzDQoNCiMgbG9vcCB0aHJvdWdoIGVhY2ggc2VsZWN0aW9uIG5ldHdvcms7DQojIGluc3BlY3QgdGhlIGV2b2x1dGlvbiBvZiBNSSBhbmQgVVAsIGZvciBib3RoIHdlYWsgYW5kIHN0cm9uZyBpbmZsdWVuY2Ugc2NlbmFyaW9zDQpmb3IgKGkgaW4gaW5kKSB7DQogICMgYWNjZXNzIG5ldHdvcmsgYW5kIHN0YXRpc3RpY3MNCiAgbmV0d29yayA8LSBuZXRzW1tpXV0kbmV0d29yaw0KICBkaXN0IDwtIG5ldHNbW2ldXSRkaXN0DQogIGFscGhhIDwtIG5ldHNbW2ldXSRhbHBoYQ0KICBhY3R1YWxfciA8LSBuZXRzW1tpXV0kYWN0dWFsX3INCiAgYWN0dWFsX3JobyA8LSBuZXRzW1tpXV0kYWN0dWFsX3Jobw0KICBtaSA8LSBuZXRzW1tpXV0kbWkNCiAgDQogICMgcHJpbnQgc3RhdGlzdGljcw0KICBjYXQoIlNlbGVjdGVkIG5ldHdvcmsiLCBpLCAiXG4iKQ0KICBjYXQoIkRpc3RyaWJ1dGlvbjoiLCBkaXN0LCAiXG4iKQ0KICBpZihkaXN0ID09ICJwb3dlci1sYXciKXtjYXQoIs6xOiIsIGFscGhhLCAiXG4iKX0NCiAgY2F0KCJyX3tra306IiwgYWN0dWFsX3IsICJcbiIpDQogIGNhdCgiz4Ffe2t4fToiLCBhY3R1YWxfcmhvLCAiXG4iKQ0KICBjYXQoIlN0YXJ0aW5nIE1JOiIsIG1pLCAiXG4iKQ0KICANCiAgIyB3ZWFrIGluZmx1ZW5jZQ0KICBjYXQoJ1J1bm5pbmcgc2ltdWxhdGlvbiBmb3IgIndlYWsgaW5mbHVlbmNlIiBzY2VuYXJpby4uLlxuJykNCiAgcHJpbnQoZmFibShuZXR3b3JrPW5ldHdvcmssIHJvdW5kcz0yMCwgcGFyYW1zPWxpc3Qocz0xMCwgZT0xMCwgdz0zMCwgej00MCksIHBsb3Q9VFJVRSkkcGxvdCkNCiAgDQogICMgc3Ryb25nIGluZmx1ZW5jZQ0KICBjYXQoJ1J1bm5pbmcgc2ltdWxhdGlvbiBmb3IgInN0cm9uZyBpbmZsdWVuY2UiIHNjZW5hcmlvLi4uXG4nKQ0KICBwcmludChmYWJtKG5ldHdvcms9bmV0d29yaywgcm91bmRzPTIwLCBwYXJhbXM9bGlzdChzPTEwLCBlPTEwLCB3PTMwLCB6PTYwKSwgcGxvdD1UUlVFKSRwbG90KQ0KfQ0KYGBgIA0KDQotLS0NCg0KIyBzaW11bGF0aW9ucw0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiNjbGVhbiBvdXIgd29ya2luZyBlbnZpcm9uZW1lbnQsIGJ1dCBrZWVwIG91ciBmdW5jdGlvbnMsIG91ciBkYXRhZnJhbWUsIGFuZCBvdXIgbGlzdCBvZiBzdGFydGluZyBuZXR3b3Jrcw0Kcm0obGlzdCA9IHNldGRpZmYoIGxzKCksIGMoImRmIiwgIm5ldHMiLCBsc2Yuc3RyKCkpKSApDQpnYygpDQoNCiMgQFJGOiBsZXQncyBzdGFydCBub3Qgd2l0aCBhbGwgbmV0d29ya3MuLi4gYnV0IHNlbGVjdCBmb3IgZWFjaCBwYXJhbWV0ZXIgc3BhY2Ugcm93IGEgZmV3IHNlZWRzDQpzdWJfbmV0cyA8LSBGaWx0ZXIoZnVuY3Rpb24oeCkgeCRzZWVkICVpbiUgYygxMjQpLCBuZXRzKQ0KDQojIHBhcmFtZXRlcnMNCnJvdW5kcyA9IDEwDQp6MSA9IDQwICMgd2VhayBpbmZsdWVuY2UNCnoyID0gNjAgIyBzdHJvbmcgaW5mbHVlbmNlDQoNCiMgcGFyYWxsZWwgYmFja2VuZA0KbmNvcmVzIDwtIGRldGVjdENvcmVzKCkgLSAxIA0KY2wgPC0gbWFrZUNsdXN0ZXIobmNvcmVzKQ0KcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQ0KDQojIGV4cG9ydCBjdXN0b20gZnVuY3Rpb25zIGFuZCByZXF1aXJlZCBvYmplY3RzDQpjbHVzdGVyRXhwb3J0KGNsLCBjKCJmYWJtIiwgImZ1dGlsaXR5IiwgInN1Yl9uZXRzIiwgInJvdW5kcyIsICJ6MSIsICJ6MiIpKQ0KDQojIHJ1biBsb29wIGluIHBhcmFsbGVsDQpzeXN0ZW0udGltZSh7DQogIHJlc3VsdHMgPC0gZm9yZWFjaChpID0gMTpsZW5ndGgoc3ViX25ldHMpLCAuY29tYmluZSA9ICdyYmluZCcsIC5wYWNrYWdlcyA9IGMoImlncmFwaCIsICJ0aWR5dmVyc2UiKSkgJWRvcGFyJSB7DQogIA0KICAgICMgc2ltdWxhdGlvbiBmb3Igd2VhayBpbmZsdWVuY2Ugc2NlbmFyaW8NCiAgICBzaW1fd2VhayA8LSBmYWJtKG5ldHdvcmsgPSBzdWJfbmV0c1tbaV1dJG5ldHdvcmssIHJvdW5kcyA9IHJvdW5kcywgcGFyYW1zID0gbGlzdChzID0gMTAsIGUgPSAxMCwgdyA9IDMwLCB6ID0gejEpKSRldm8NCiAgICBvdXRjb21lX3dlYWsgPC0gc2ltX3dlYWskc3RhdGlzdGljW3NpbV93ZWFrJHJvdW5kID09IHJvdW5kcyAmIHNpbV93ZWFrJG91dGNvbWUgPT0gImZvbGxvd190cmVuZCJdDQogICAgDQogICAgIyBzaW11bGF0aW9uIGZvciBzdHJvbmcgaW5mbHVlbmNlIHNjZW5hcmlvDQogICAgc2ltX3N0cm9uZyA8LSBmYWJtKG5ldHdvcmsgPSBzdWJfbmV0c1tbaV1dJG5ldHdvcmssIHJvdW5kcyA9IHJvdW5kcywgcGFyYW1zID0gbGlzdChzID0gMTAsIGUgPSAxMCwgdyA9IDMwLCB6ID0gejIpKSRldm8NCiAgICBvdXRjb21lX3N0cm9uZyA8LSBzaW1fc3Ryb25nJHN0YXRpc3RpY1tzaW1fc3Ryb25nJHJvdW5kID09IHJvdW5kcyAmIHNpbV9zdHJvbmckb3V0Y29tZSA9PSAiZm9sbG93X3RyZW5kIl0NCiAgICANCiAgICBnYygpDQogICAgDQogICAgIyByZXR1cm4gcmVzdWx0cyBhcyBhIHJvdyB0byBiZSByYmluZGVkIGluIGEgcmVzdWx0cyBtYXRyaXgNCiAgICByZXR1cm4oYyhvdXRjb21lX3dlYWssIG91dGNvbWVfc3Ryb25nKSkNCiAgfQ0KfSkNCg0Kc3RvcENsdXN0ZXIoY2wpDQpjbG9zZUFsbENvbm5lY3Rpb25zKCkNCg0KDQojYW5kIGFkZCB0aGVzZSBvdXRjb21lcyB0byBvdXIgZGF0YWZyYW1lOg0KI2RmJG91dGNvbWVfd2VhayA8LSBvdXRjb21lX3dlYWsNCiNkZiRvdXRjb21lX3N0cm9uZyA8LSBvdXRjb21lX3N0cm9uZw0KDQoNCmBgYCANCg0KDQoNCg0KLS0tDQoNCiMgUmVmZXJlbmNlcw0KDQo=


Copyright © Rob Franken